edgefirst-tflite 0.7.0

Ergonomic Rust API for TensorFlow Lite with DMABUF zero-copy and NPU preprocessing
Documentation
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2025 Au-Zone Technologies. All Rights Reserved.

//! Safe wrapper around the `TFLite` shared-library handle.

use std::fmt;
use std::path::{Path, PathBuf};

use crate::error::{Error, Result};

/// Handle to a loaded `TFLite` shared library.
///
/// `Library` wraps the FFI function table produced by `libloading` and
/// `bindgen`, providing safe construction via auto-discovery or an explicit
/// filesystem path.
///
/// # Examples
///
/// ```no_run
/// use edgefirst_tflite::Library;
///
/// // Auto-discover TFLite library
/// let lib = Library::new()?;
///
/// // Or load from a specific path
/// let lib = Library::from_path("/usr/lib/libtensorflowlite_c.so")?;
/// # Ok::<(), edgefirst_tflite::Error>(())
/// ```
pub struct Library {
    inner: edgefirst_tflite_sys::tensorflowlite_c,
    path: Option<PathBuf>,
}

impl Library {
    /// Discover and load the `TFLite` shared library automatically.
    ///
    /// This probes well-known versioned and unversioned library paths using
    /// the [`edgefirst_tflite_sys::discovery`] module.
    ///
    /// # Errors
    ///
    /// Returns an [`Error`] if no compatible `TFLite` library can be found.
    pub fn new() -> Result<Self> {
        let (inner, path) =
            edgefirst_tflite_sys::discovery::discover_with_path().map_err(Error::from)?;
        Ok(Self {
            inner,
            path: Some(path),
        })
    }

    /// Load the `TFLite` shared library from a specific `path`.
    ///
    /// # Errors
    ///
    /// Returns an [`Error`] if the library cannot be loaded from `path` or
    /// required symbols are missing.
    pub fn from_path(path: impl AsRef<Path>) -> Result<Self> {
        let raw = path.as_ref();
        let inner = edgefirst_tflite_sys::discovery::load(raw).map_err(Error::from)?;
        // Canonicalise so reopen() works even if the working directory
        // changes later. Fall back to the original for soname-only paths.
        let resolved = if raw.is_file() {
            std::fs::canonicalize(raw).unwrap_or_else(|_| raw.to_path_buf())
        } else {
            raw.to_path_buf()
        };
        Ok(Self {
            inner,
            path: Some(resolved),
        })
    }

    /// Returns a reference to the underlying FFI function table.
    ///
    /// This is an escape hatch for advanced users who need direct access to
    /// the raw `tensorflowlite_c` bindings.
    #[must_use]
    pub fn as_sys(&self) -> &edgefirst_tflite_sys::tensorflowlite_c {
        &self.inner
    }

    /// Re-open the underlying shared library, incrementing the OS refcount.
    ///
    /// This is used internally to keep the main `TFLite` library alive for
    /// built-in delegates (e.g., XNNPACK) whose function pointers live in
    /// the main library rather than a separate delegate `.so`.
    pub(crate) fn reopen(&self) -> Result<libloading::Library> {
        let path = self
            .path
            .as_ref()
            .ok_or_else(|| Error::invalid_argument("library path not available for reopen"))?;
        // SAFETY: Re-opening the same shared library increments the OS
        // refcount. The path is known-valid because it was successfully
        // loaded during construction.
        unsafe { libloading::Library::new(path.as_os_str()) }.map_err(Error::from)
    }
}

// SAFETY: `Library` holds a `tensorflowlite_c` struct whose fields are
// function pointers (all `Send + Sync`) and a `libloading::Library` (which
// is `Send + Sync`). The TFLite C API has no thread affinity — function
// pointers resolved from a loaded library are safe to call from any thread.
unsafe impl Send for Library {}

// SAFETY: All access through `&Library` is via immutable function-pointer
// calls (`as_sys()` returns `&tensorflowlite_c`). No interior mutability.
unsafe impl Sync for Library {}

impl fmt::Debug for Library {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Library")
            .field("inner", &"tensorflowlite_c { .. }")
            .finish()
    }
}