Skip to main content

edgefirst_tflite/
library.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 Au-Zone Technologies. All Rights Reserved.
3
4//! Safe wrapper around the `TFLite` shared-library handle.
5
6use std::fmt;
7use std::path::{Path, PathBuf};
8
9use crate::error::{Error, Result};
10
11/// Handle to a loaded `TFLite` shared library.
12///
13/// `Library` wraps the FFI function table produced by `libloading` and
14/// `bindgen`, providing safe construction via auto-discovery or an explicit
15/// filesystem path.
16///
17/// # Examples
18///
19/// ```no_run
20/// use edgefirst_tflite::Library;
21///
22/// // Auto-discover TFLite library
23/// let lib = Library::new()?;
24///
25/// // Or load from a specific path
26/// let lib = Library::from_path("/usr/lib/libtensorflowlite_c.so")?;
27/// # Ok::<(), edgefirst_tflite::Error>(())
28/// ```
29pub struct Library {
30    inner: edgefirst_tflite_sys::tensorflowlite_c,
31    path: Option<PathBuf>,
32}
33
34impl Library {
35    /// Discover and load the `TFLite` shared library automatically.
36    ///
37    /// This probes well-known versioned and unversioned library paths using
38    /// the [`edgefirst_tflite_sys::discovery`] module.
39    ///
40    /// # Errors
41    ///
42    /// Returns an [`Error`] if no compatible `TFLite` library can be found.
43    pub fn new() -> Result<Self> {
44        let (inner, path) =
45            edgefirst_tflite_sys::discovery::discover_with_path().map_err(Error::from)?;
46        Ok(Self {
47            inner,
48            path: Some(path),
49        })
50    }
51
52    /// Load the `TFLite` shared library from a specific `path`.
53    ///
54    /// # Errors
55    ///
56    /// Returns an [`Error`] if the library cannot be loaded from `path` or
57    /// required symbols are missing.
58    pub fn from_path(path: impl AsRef<Path>) -> Result<Self> {
59        let raw = path.as_ref();
60        let inner = edgefirst_tflite_sys::discovery::load(raw).map_err(Error::from)?;
61        // Canonicalise so reopen() works even if the working directory
62        // changes later. Fall back to the original for soname-only paths.
63        let resolved = if raw.is_file() {
64            std::fs::canonicalize(raw).unwrap_or_else(|_| raw.to_path_buf())
65        } else {
66            raw.to_path_buf()
67        };
68        Ok(Self {
69            inner,
70            path: Some(resolved),
71        })
72    }
73
74    /// Returns a reference to the underlying FFI function table.
75    ///
76    /// This is an escape hatch for advanced users who need direct access to
77    /// the raw `tensorflowlite_c` bindings.
78    #[must_use]
79    pub fn as_sys(&self) -> &edgefirst_tflite_sys::tensorflowlite_c {
80        &self.inner
81    }
82
83    /// Re-open the underlying shared library, incrementing the OS refcount.
84    ///
85    /// This is used internally to keep the main `TFLite` library alive for
86    /// built-in delegates (e.g., XNNPACK) whose function pointers live in
87    /// the main library rather than a separate delegate `.so`.
88    pub(crate) fn reopen(&self) -> Result<libloading::Library> {
89        let path = self
90            .path
91            .as_ref()
92            .ok_or_else(|| Error::invalid_argument("library path not available for reopen"))?;
93        // SAFETY: Re-opening the same shared library increments the OS
94        // refcount. The path is known-valid because it was successfully
95        // loaded during construction.
96        unsafe { libloading::Library::new(path.as_os_str()) }.map_err(Error::from)
97    }
98}
99
100// SAFETY: `Library` holds a `tensorflowlite_c` struct whose fields are
101// function pointers (all `Send + Sync`) and a `libloading::Library` (which
102// is `Send + Sync`). The TFLite C API has no thread affinity — function
103// pointers resolved from a loaded library are safe to call from any thread.
104unsafe impl Send for Library {}
105
106// SAFETY: All access through `&Library` is via immutable function-pointer
107// calls (`as_sys()` returns `&tensorflowlite_c`). No interior mutability.
108unsafe impl Sync for Library {}
109
110impl fmt::Debug for Library {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        f.debug_struct("Library")
113            .field("inner", &"tensorflowlite_c { .. }")
114            .finish()
115    }
116}