grit-core 0.1.0

Embedded, bi-temporal property graph for agent memory: one SQLite file, in-process, deterministic
Documentation
//! The crate-wide `#![deny(unsafe_code)]` is lifted for exactly this module:
//! the sqlite-vec FFI registration (see AGENTS.md "Style"). Nothing else in
//! grit-core may use `unsafe`.
#![allow(unsafe_code)]

use std::sync::Once;

/// Register sqlite-vec as an auto-extension so every subsequently opened
/// connection (writer and readers alike) has the `vec0` module available.
/// Idempotent; safe to call from multiple threads.
pub(crate) fn register_sqlite_vec() {
    static ONCE: Once = Once::new();
    ONCE.call_once(|| {
        type SqliteInitFn = unsafe extern "C" fn(
            *mut rusqlite::ffi::sqlite3,
            *mut *mut std::os::raw::c_char,
            *const rusqlite::ffi::sqlite3_api_routines,
        ) -> std::os::raw::c_int;
        // SAFETY: sqlite3_vec_init has the sqlite3 extension-init ABI that
        // sqlite3_auto_extension expects; the cast is the pattern documented
        // by sqlite-vec itself. Called once, before any connection is opened.
        unsafe {
            rusqlite::ffi::sqlite3_auto_extension(Some(std::mem::transmute::<
                *const (),
                SqliteInitFn,
            >(
                sqlite_vec::sqlite3_vec_init as *const (),
            )));
        }
    });
}

/// View a float slice as little-endian bytes for binding to a `vec0` column.
/// (SQLite blobs are byte-addressed; sqlite-vec reads float32 LE.)
pub(crate) fn f32s_as_bytes(v: &[f32]) -> Vec<u8> {
    let mut out = Vec::with_capacity(v.len() * 4);
    for x in v {
        out.extend_from_slice(&x.to_le_bytes());
    }
    out
}