1#![allow(clippy::missing_safety_doc)]
6
7use std::ffi::{c_char, c_void};
8
9use crate::kernel::{KernelLanguage, KernelVTable};
10use crate::runtime::{self};
11use crate::status::Error;
12
13use super::*;
14
15#[repr(C)]
18#[derive(Clone, Copy)]
19pub struct LMFlowKernelVTable {
20 pub create: Option<unsafe extern "C" fn(*mut c_void) -> *mut c_void>,
21 pub get_contract: Option<unsafe extern "C" fn(*mut c_void, *mut LMFlowContract)>,
22 pub open: Option<unsafe extern "C" fn(*mut c_void, *mut LMFlowContext) -> i32>,
23 pub process: Option<unsafe extern "C" fn(*mut c_void, *mut LMFlowContext) -> i32>,
24 pub close: Option<unsafe extern "C" fn(*mut c_void, *mut LMFlowContext) -> i32>,
25 pub destroy: Option<unsafe extern "C" fn(*mut c_void)>,
26}
27
28#[no_mangle]
29pub unsafe extern "C" fn lmflow_register_kernel(
30 name: *const c_char,
31 vt: *const LMFlowKernelVTable,
32 factory: *mut c_void,
33) -> i32 {
34 register_kernel_impl(name, vt, factory, KernelLanguage::Unknown)
35}
36
37#[no_mangle]
38pub unsafe extern "C" fn lmflow_register_kernel_with_language(
39 name: *const c_char,
40 vt: *const LMFlowKernelVTable,
41 factory: *mut c_void,
42 language: u32,
43) -> i32 {
44 let language = match language {
45 0 => KernelLanguage::Unknown,
46 1 => KernelLanguage::Rust,
47 2 => KernelLanguage::Cpp,
48 3 => KernelLanguage::Python,
49 4 => KernelLanguage::C,
50 other => {
51 return guard(|| {
52 fail(Error::InvalidArg(format!(
53 "unknown kernel implementation language id {other}"
54 )))
55 })
56 }
57 };
58 register_kernel_impl(name, vt, factory, language)
59}
60
61unsafe fn register_kernel_impl(
62 name: *const c_char,
63 vt: *const LMFlowKernelVTable,
64 factory: *mut c_void,
65 language: KernelLanguage,
66) -> i32 {
67 guard(|| {
68 let Some(n) = cstr(name) else {
69 return fail(Error::InvalidArg(
70 "kernel name is empty or not UTF-8".into(),
71 ));
72 };
73 if vt.is_null() {
74 return fail(Error::InvalidArg(format!("kernel `{n}` vtable is null")));
75 }
76 let v = &*vt;
77 type CtxFn = unsafe extern "C" fn(*mut c_void, *mut c_void) -> i32;
81 type ContractFn = unsafe extern "C" fn(*mut c_void, *mut c_void);
82 let converted = KernelVTable {
83 create: v.create,
84 get_contract: std::mem::transmute::<
85 Option<unsafe extern "C" fn(*mut c_void, *mut LMFlowContract)>,
86 Option<ContractFn>,
87 >(v.get_contract),
88 open: std::mem::transmute::<
89 Option<unsafe extern "C" fn(*mut c_void, *mut LMFlowContext) -> i32>,
90 Option<CtxFn>,
91 >(v.open),
92 process: std::mem::transmute::<
93 Option<unsafe extern "C" fn(*mut c_void, *mut LMFlowContext) -> i32>,
94 Option<CtxFn>,
95 >(v.process),
96 close: std::mem::transmute::<
97 Option<unsafe extern "C" fn(*mut c_void, *mut LMFlowContext) -> i32>,
98 Option<CtxFn>,
99 >(v.close),
100 destroy: v.destroy,
101 };
102 to_status(crate::kernel::register_with_language(
103 n, converted, factory, language,
104 ))
105 })
106}
107
108#[no_mangle]
111pub unsafe extern "C" fn lmflow_contract_num_inputs(c: *const LMFlowContract) -> usize {
112 guard_val(0, || {
113 contract_mut(c as *mut LMFlowContract).map_or(0, |x| x.inputs.len())
114 })
115}
116#[no_mangle]
117pub unsafe extern "C" fn lmflow_contract_num_outputs(c: *const LMFlowContract) -> usize {
118 guard_val(0, || {
119 contract_mut(c as *mut LMFlowContract).map_or(0, |x| x.outputs.len())
120 })
121}
122#[no_mangle]
123pub unsafe extern "C" fn lmflow_contract_input_id(
124 c: *const LMFlowContract,
125 tag: *const c_char,
126 index: usize,
127) -> usize {
128 guard_val(INVALID_ID, || {
129 let Some(x) = contract_mut(c as *mut LMFlowContract) else {
130 return INVALID_ID;
131 };
132 x.inputs
133 .id_by_tag(cstr(tag).unwrap_or(""), index)
134 .unwrap_or(INVALID_ID)
135 })
136}
137#[no_mangle]
138pub unsafe extern "C" fn lmflow_contract_output_id(
139 c: *const LMFlowContract,
140 tag: *const c_char,
141 index: usize,
142) -> usize {
143 guard_val(INVALID_ID, || {
144 let Some(x) = contract_mut(c as *mut LMFlowContract) else {
145 return INVALID_ID;
146 };
147 x.outputs
148 .id_by_tag(cstr(tag).unwrap_or(""), index)
149 .unwrap_or(INVALID_ID)
150 })
151}
152#[no_mangle]
153pub unsafe extern "C" fn lmflow_contract_input_index(
154 c: *const LMFlowContract,
155 name: *const c_char,
156) -> usize {
157 guard_val(INVALID_ID, || {
158 let Some(x) = contract_mut(c as *mut LMFlowContract) else {
159 return INVALID_ID;
160 };
161 x.inputs
162 .index_by_name(cstr(name).unwrap_or(""))
163 .unwrap_or(INVALID_ID)
164 })
165}
166#[no_mangle]
167pub unsafe extern "C" fn lmflow_contract_output_index(
168 c: *const LMFlowContract,
169 name: *const c_char,
170) -> usize {
171 guard_val(INVALID_ID, || {
172 let Some(x) = contract_mut(c as *mut LMFlowContract) else {
173 return INVALID_ID;
174 };
175 x.outputs
176 .index_by_name(cstr(name).unwrap_or(""))
177 .unwrap_or(INVALID_ID)
178 })
179}
180
181fn contract_arena() -> &'static runtime::CStrArena {
183 static A: std::sync::LazyLock<runtime::CStrArena> =
184 std::sync::LazyLock::new(runtime::CStrArena::default);
185 &A
186}
187
188#[no_mangle]
189pub unsafe extern "C" fn lmflow_contract_input_name(
190 c: *const LMFlowContract,
191 idx: usize,
192) -> *const c_char {
193 guard_val(c"".as_ptr(), || {
194 contract_mut(c as *mut LMFlowContract)
195 .and_then(|x| x.inputs.name(idx).map(|s| contract_arena().intern(s)))
196 .unwrap_or(c"".as_ptr())
197 })
198}
199#[no_mangle]
200pub unsafe extern "C" fn lmflow_contract_output_name(
201 c: *const LMFlowContract,
202 idx: usize,
203) -> *const c_char {
204 guard_val(c"".as_ptr(), || {
205 contract_mut(c as *mut LMFlowContract)
206 .and_then(|x| x.outputs.name(idx).map(|s| contract_arena().intern(s)))
207 .unwrap_or(c"".as_ptr())
208 })
209}
210
211#[no_mangle]
212pub unsafe extern "C" fn lmflow_contract_input_set_any(c: *mut LMFlowContract, idx: usize) {
213 guard_val((), || {
214 if let Some(x) = contract_mut(c) {
215 if let Some(s) = x.input_types.get_mut(idx) {
216 *s = 0;
217 } else {
218 x.record_error(format!(
219 "input port index {idx} is out of range (num_inputs={})",
220 x.input_types.len()
221 ));
222 }
223 }
224 });
225}
226#[no_mangle]
227pub unsafe extern "C" fn lmflow_contract_output_set_any(c: *mut LMFlowContract, idx: usize) {
228 guard_val((), || {
229 if let Some(x) = contract_mut(c) {
230 if let Some(s) = x.output_types.get_mut(idx) {
231 *s = 0;
232 } else {
233 x.record_error(format!(
234 "output port index {idx} is out of range (num_outputs={})",
235 x.output_types.len()
236 ));
237 }
238 }
239 });
240}
241#[no_mangle]
242pub unsafe extern "C" fn lmflow_contract_input_set_type(
243 c: *mut LMFlowContract,
244 idx: usize,
245 type_id: u64,
246) {
247 guard_val((), || {
248 if let Some(x) = contract_mut(c) {
249 if let Some(s) = x.input_types.get_mut(idx) {
250 *s = type_id;
251 } else {
252 x.record_error(format!(
253 "input port index {idx} is out of range (num_inputs={})",
254 x.input_types.len()
255 ));
256 }
257 }
258 });
259}
260#[no_mangle]
261pub unsafe extern "C" fn lmflow_contract_output_set_type(
262 c: *mut LMFlowContract,
263 idx: usize,
264 type_id: u64,
265) {
266 guard_val((), || {
267 if let Some(x) = contract_mut(c) {
268 if let Some(s) = x.output_types.get_mut(idx) {
269 *s = type_id;
270 } else {
271 x.record_error(format!(
272 "output port index {idx} is out of range (num_outputs={})",
273 x.output_types.len()
274 ));
275 }
276 }
277 });
278}
279#[no_mangle]
280pub unsafe extern "C" fn lmflow_contract_require_side_packet(
281 c: *mut LMFlowContract,
282 name: *const c_char,
283) {
284 guard_val((), || {
285 if let Some(x) = contract_mut(c) {
286 if let Some(name) = cstr(name).filter(|name| !name.is_empty()) {
287 x.required_side_packets.push(name.to_string());
288 } else {
289 x.record_error("required side packet name must not be empty or invalid UTF-8");
290 }
291 }
292 });
293}
294
295#[no_mangle]
296pub unsafe extern "C" fn lmflow_contract_set_error(c: *mut LMFlowContract, message: *const c_char) {
297 guard_val((), || {
298 if let Some(x) = contract_mut(c) {
299 let message = cstr(message)
300 .filter(|message| !message.is_empty())
301 .unwrap_or("GetContract failed without a valid UTF-8 error message");
302 x.record_error(message);
303 }
304 });
305}