edgefirst_tflite/
delegate.rs1use std::ffi::CString;
11use std::path::Path;
12use std::ptr::{self, NonNull};
13
14use edgefirst_tflite_sys::TfLiteDelegate;
15
16use crate::error::{Error, Result};
17
18#[cfg(feature = "dmabuf")]
19use edgefirst_tflite_sys::vx_ffi::VxDmaBufFunctions;
20
21#[cfg(feature = "camera_adaptor")]
22use edgefirst_tflite_sys::vx_ffi::VxCameraAdaptorFunctions;
23
24#[derive(Debug, Default, Clone)]
40pub struct DelegateOptions {
41 options: Vec<(String, String)>,
42}
43
44impl DelegateOptions {
45 #[must_use]
47 pub fn new() -> Self {
48 Self::default()
49 }
50
51 #[must_use]
53 pub fn option(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
54 self.options.push((key.into(), value.into()));
55 self
56 }
57}
58
59#[allow(clippy::struct_field_names)]
87pub struct Delegate {
88 delegate: NonNull<TfLiteDelegate>,
89 free: unsafe extern "C" fn(*mut TfLiteDelegate),
90 _lib: libloading::Library,
92
93 #[cfg(feature = "dmabuf")]
94 dmabuf_fns: Option<VxDmaBufFunctions>,
95
96 #[cfg(feature = "camera_adaptor")]
97 camera_adaptor_fns: Option<VxCameraAdaptorFunctions>,
98}
99
100impl Delegate {
101 pub fn load(path: impl AsRef<Path>) -> Result<Self> {
108 Self::load_with_options(path, &DelegateOptions::default())
109 }
110
111 pub fn load_with_options(path: impl AsRef<Path>, options: &DelegateOptions) -> Result<Self> {
119 let lib =
122 unsafe { libloading::Library::new(path.as_ref().as_os_str()) }.map_err(Error::from)?;
123
124 let create_fn = unsafe {
127 lib.get::<unsafe extern "C" fn(
128 *const *const std::os::raw::c_char,
129 *const *const std::os::raw::c_char,
130 usize,
131 Option<unsafe extern "C" fn(*const std::os::raw::c_char)>,
132 ) -> *mut TfLiteDelegate>(b"tflite_plugin_create_delegate")
133 }
134 .map_err(Error::from)?;
135
136 let destroy_fn = unsafe {
139 lib.get::<unsafe extern "C" fn(*mut TfLiteDelegate)>(b"tflite_plugin_destroy_delegate")
140 }
141 .map_err(Error::from)?;
142
143 let (keys_c, values_c): (Vec<CString>, Vec<CString>) = options
145 .options
146 .iter()
147 .map(|(k, v)| {
148 Ok((
149 CString::new(k.as_str()).map_err(|_| {
150 Error::invalid_argument(format!(
151 "option key \"{k}\" contains interior NUL byte"
152 ))
153 })?,
154 CString::new(v.as_str()).map_err(|_| {
155 Error::invalid_argument(format!(
156 "option value \"{v}\" contains interior NUL byte"
157 ))
158 })?,
159 ))
160 })
161 .collect::<Result<Vec<_>>>()?
162 .into_iter()
163 .unzip();
164 let keys_ptrs: Vec<*const std::os::raw::c_char> =
165 keys_c.iter().map(|c| c.as_ptr()).collect();
166 let values_ptrs: Vec<*const std::os::raw::c_char> =
167 values_c.iter().map(|c| c.as_ptr()).collect();
168
169 let raw = unsafe {
174 create_fn(
175 if keys_ptrs.is_empty() {
176 ptr::null()
177 } else {
178 keys_ptrs.as_ptr()
179 },
180 if values_ptrs.is_empty() {
181 ptr::null()
182 } else {
183 values_ptrs.as_ptr()
184 },
185 options.options.len(),
186 None,
187 )
188 };
189
190 let delegate = NonNull::new(raw)
191 .ok_or_else(|| Error::null_pointer("tflite_plugin_create_delegate returned null"))?;
192
193 let free = *destroy_fn;
195
196 #[cfg(feature = "dmabuf")]
198 let dmabuf_fns = unsafe { VxDmaBufFunctions::try_load(&lib) };
201
202 #[cfg(feature = "camera_adaptor")]
203 let camera_adaptor_fns = unsafe { VxCameraAdaptorFunctions::try_load(&lib) };
206
207 Ok(Self {
208 delegate,
209 free,
210 _lib: lib,
211 #[cfg(feature = "dmabuf")]
212 dmabuf_fns,
213 #[cfg(feature = "camera_adaptor")]
214 camera_adaptor_fns,
215 })
216 }
217
218 #[must_use]
223 pub fn as_ptr(&self) -> *mut TfLiteDelegate {
224 self.delegate.as_ptr()
225 }
226
227 #[cfg(feature = "dmabuf")]
229 #[must_use]
230 pub fn dmabuf(&self) -> Option<crate::dmabuf::DmaBuf<'_>> {
231 self.dmabuf_fns
232 .as_ref()
233 .map(|fns| crate::dmabuf::DmaBuf::new(self.delegate, fns))
234 }
235
236 #[cfg(feature = "dmabuf")]
238 #[must_use]
239 pub fn has_dmabuf(&self) -> bool {
240 self.dmabuf_fns.is_some()
241 }
242
243 #[cfg(feature = "camera_adaptor")]
245 #[must_use]
246 pub fn camera_adaptor(&self) -> Option<crate::camera_adaptor::CameraAdaptor<'_>> {
247 self.camera_adaptor_fns
248 .as_ref()
249 .map(|fns| crate::camera_adaptor::CameraAdaptor::new(self.delegate, fns))
250 }
251
252 #[cfg(feature = "camera_adaptor")]
254 #[must_use]
255 pub fn has_camera_adaptor(&self) -> bool {
256 self.camera_adaptor_fns.is_some()
257 }
258}
259
260#[allow(clippy::missing_fields_in_debug)]
261impl std::fmt::Debug for Delegate {
262 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
263 let mut d = f.debug_struct("Delegate");
264 d.field("ptr", &self.delegate);
265
266 #[cfg(feature = "dmabuf")]
267 d.field("has_dmabuf", &self.dmabuf_fns.is_some());
268
269 #[cfg(feature = "camera_adaptor")]
270 d.field("has_camera_adaptor", &self.camera_adaptor_fns.is_some());
271
272 d.finish_non_exhaustive()
273 }
274}
275
276impl Drop for Delegate {
277 fn drop(&mut self) {
278 unsafe { (self.free)(self.delegate.as_ptr()) };
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288
289 #[test]
290 fn new_creates_empty_options() {
291 let opts = DelegateOptions::new();
292 let debug = format!("{opts:?}");
293 assert_eq!(debug, "DelegateOptions { options: [] }");
294 }
295
296 #[test]
297 fn builder_chaining() {
298 let opts = DelegateOptions::new().option("a", "1").option("b", "2");
299 assert_eq!(opts.options.len(), 2);
300 }
301
302 #[test]
303 fn default_matches_new() {
304 let from_new = format!("{:?}", DelegateOptions::new());
305 let from_default = format!("{:?}", DelegateOptions::default());
306 assert_eq!(from_new, from_default);
307 }
308
309 #[test]
310 fn clone_produces_equal_values() {
311 let opts = DelegateOptions::new().option("key", "value");
312 let cloned = opts.clone();
313 assert_eq!(format!("{opts:?}"), format!("{cloned:?}"));
314 }
315
316 #[test]
317 fn debug_formatting_not_empty() {
318 let opts = DelegateOptions::new().option("cache", "/tmp");
319 let debug = format!("{opts:?}");
320 assert!(!debug.is_empty());
321 assert!(debug.contains("DelegateOptions"));
322 assert!(debug.contains("cache"));
323 assert!(debug.contains("/tmp"));
324 }
325}