use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_int};
use std::ptr;
use std::sync::{Arc, Mutex};
use crate::corpus::ConCorpus;
use crate::keys::{hash_frame_bytes, ContentHash, FrameKey};
use crate::select::Select;
pub const RKRDB_OK: c_int = 0;
pub const RKRDB_ERR: c_int = -1;
pub const RKRDB_NOT_FOUND: c_int = -2;
pub const RKRDB_NULL: c_int = -3;
struct Handle {
corpus: Arc<ConCorpus>,
last_keys: Vec<FrameKey>,
last_error: String,
}
static HANDLES: Mutex<Vec<Option<Box<Handle>>>> = Mutex::new(Vec::new());
fn push_handle(h: Handle) -> usize {
let mut g = HANDLES.lock().unwrap();
for (i, slot) in g.iter_mut().enumerate() {
if slot.is_none() {
*slot = Some(Box::new(h));
return i;
}
}
g.push(Some(Box::new(h)));
g.len() - 1
}
fn with_handle<F, T>(id: usize, f: F) -> Result<T, c_int>
where
F: FnOnce(&mut Handle) -> Result<T, c_int>,
{
let mut g = HANDLES.lock().unwrap();
let slot = g.get_mut(id).ok_or(RKRDB_NULL)?;
let h = slot.as_mut().ok_or(RKRDB_NULL)?;
f(h)
}
fn corpus_arc(id: usize) -> Result<Arc<ConCorpus>, c_int> {
let g = HANDLES.lock().unwrap();
let slot = g.get(id).ok_or(RKRDB_NULL)?;
let h = slot.as_ref().ok_or(RKRDB_NULL)?;
Ok(Arc::clone(&h.corpus))
}
fn set_err_id(id: usize, e: impl ToString) {
let mut g = HANDLES.lock().unwrap();
if let Some(Some(h)) = g.get_mut(id) {
h.last_error = e.to_string();
}
}
fn set_err(h: &mut Handle, e: impl ToString) {
h.last_error = e.to_string();
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_open(path: *const c_char, out_id: *mut usize) -> c_int {
if path.is_null() || out_id.is_null() {
return RKRDB_NULL;
}
let cpath = unsafe { CStr::from_ptr(path) };
let path = match cpath.to_str() {
Ok(s) => s,
Err(_) => return RKRDB_ERR,
};
match ConCorpus::open(path) {
Ok(corpus) => {
let id = push_handle(Handle {
corpus: Arc::new(corpus),
last_keys: Vec::new(),
last_error: String::new(),
});
unsafe { *out_id = id };
RKRDB_OK
}
Err(_) => RKRDB_ERR,
}
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_close(id: usize) -> c_int {
let mut g = HANDLES.lock().unwrap();
if let Some(slot) = g.get_mut(id) {
*slot = None;
RKRDB_OK
} else {
RKRDB_NULL
}
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_last_error(id: usize, buf: *mut c_char, buflen: usize) -> c_int {
if buf.is_null() || buflen == 0 {
return RKRDB_NULL;
}
with_handle(id, |h| {
let bytes = h.last_error.as_bytes();
let n = (buflen - 1).min(bytes.len());
unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), buf as *mut u8, n);
*buf.add(n) = 0;
}
Ok(n as c_int)
})
.unwrap_or(RKRDB_NULL)
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_append_trajectory(
id: usize,
traj_id: u64,
path: *const c_char,
out_n_frames: *mut u32,
) -> c_int {
if path.is_null() {
return RKRDB_NULL;
}
let cpath = unsafe { CStr::from_ptr(path) };
let path = match cpath.to_str() {
Ok(s) => s,
Err(_) => return RKRDB_ERR,
};
let corpus = match corpus_arc(id) {
Ok(c) => c,
Err(c) => return c,
};
match corpus.append_trajectory_path(traj_id, path) {
Ok(n) => {
if !out_n_frames.is_null() {
unsafe { *out_n_frames = n };
}
RKRDB_OK
}
Err(e) => {
set_err_id(id, e);
RKRDB_ERR
}
}
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_select_basic(
id: usize,
traj_id: i64,
symbol: *const c_char,
natoms_min: u32,
natoms_max: u32,
limit: u32,
) -> c_int {
with_handle(id, |h| {
let mut sel = Select::new().natoms_range(natoms_min, natoms_max);
if traj_id >= 0 {
sel = sel.trajectory(traj_id as u64);
}
if !symbol.is_null() {
let s = unsafe { CStr::from_ptr(symbol) };
if let Ok(sym) = s.to_str() {
if !sym.is_empty() {
sel = sel.require_symbol(sym);
}
}
}
if limit > 0 {
sel = sel.limit(limit as usize);
}
match h.corpus.select(&sel) {
Ok(keys) => {
h.last_keys = keys;
Ok(RKRDB_OK)
}
Err(e) => {
set_err(h, e);
Ok(RKRDB_ERR)
}
}
})
.unwrap_or(RKRDB_NULL)
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_select_hash(id: usize, hash16: *const u8) -> c_int {
if hash16.is_null() {
return RKRDB_NULL;
}
let mut hb = [0u8; 16];
unsafe { ptr::copy_nonoverlapping(hash16, hb.as_mut_ptr(), 16) };
with_handle(id, |h| {
let sel = Select::new().exact_hash(hb);
match h.corpus.select(&sel) {
Ok(keys) => {
h.last_keys = keys;
Ok(RKRDB_OK)
}
Err(e) => {
set_err(h, e);
Ok(RKRDB_ERR)
}
}
})
.unwrap_or(RKRDB_NULL)
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_select_meta(
id: usize,
traj_id: i64,
symbol: *const c_char,
natoms_min: u32,
natoms_max: u32,
energy_min: f64,
energy_max: f64,
use_energy_range: c_int,
flags: u32,
limit: u32,
) -> c_int {
with_handle(id, |h| {
let mut sel = Select::new().natoms_range(natoms_min, natoms_max);
if traj_id >= 0 {
sel = sel.trajectory(traj_id as u64);
}
if !symbol.is_null() {
let s = unsafe { CStr::from_ptr(symbol) };
if let Ok(sym) = s.to_str() {
if !sym.is_empty() {
sel = sel.require_symbol(sym);
}
}
}
if use_energy_range != 0 {
sel = sel.energy_range(energy_min, energy_max);
}
if flags & 1 != 0 {
sel = sel.require_forces();
}
if flags & 2 != 0 {
sel = sel.require_velocities();
}
if flags & 4 != 0 {
sel = sel.require_energy();
}
if limit > 0 {
sel = sel.limit(limit as usize);
}
match h.corpus.select(&sel) {
Ok(keys) => {
h.last_keys = keys;
Ok(RKRDB_OK)
}
Err(e) => {
set_err(h, e);
Ok(RKRDB_ERR)
}
}
})
.unwrap_or(RKRDB_NULL)
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_reindex(id: usize) -> c_int {
with_handle(id, |h| match h.corpus.reindex() {
Ok(_) => Ok(RKRDB_OK),
Err(e) => {
set_err(h, e);
Ok(RKRDB_ERR)
}
})
.unwrap_or(RKRDB_NULL)
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_cook_frame(id: usize, traj_id: u64, frame_idx: u32) -> c_int {
with_handle(id, |h| {
match h.corpus.cook_frame(crate::keys::FrameKey {
traj_id,
frame_idx,
}) {
Ok(_) => Ok(RKRDB_OK),
Err(e) => {
set_err(h, e);
Ok(RKRDB_ERR)
}
}
})
.unwrap_or(RKRDB_NULL)
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_delete_cooked(id: usize, traj_id: u64, frame_idx: u32) -> c_int {
with_handle(id, |h| {
match h.corpus.delete_cooked_soa(crate::keys::FrameKey {
traj_id,
frame_idx,
}) {
Ok(()) => Ok(RKRDB_OK),
Err(e) => {
set_err(h, e);
Ok(RKRDB_ERR)
}
}
})
.unwrap_or(RKRDB_NULL)
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_has_valid_cooked(id: usize, traj_id: u64, frame_idx: u32) -> c_int {
with_handle(id, |h| {
match h.corpus.has_valid_cooked_soa(crate::keys::FrameKey {
traj_id,
frame_idx,
}) {
Ok(true) => Ok(1),
Ok(false) => Ok(0),
Err(e) => {
set_err(h, e);
Ok(RKRDB_ERR)
}
}
})
.unwrap_or(RKRDB_NULL)
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_get_positions(
id: usize,
traj_id: u64,
frame_idx: u32,
out_xyz: *mut f64,
capacity_atoms: u32,
out_natoms: *mut u32,
) -> c_int {
if out_xyz.is_null() || out_natoms.is_null() {
return RKRDB_NULL;
}
with_handle(id, |h| {
match h.corpus.get_positions(crate::keys::FrameKey {
traj_id,
frame_idx,
}) {
Ok(pos) => {
let n = pos.len() as u32;
if n > capacity_atoms {
set_err(
h,
crate::error::Error::Message("positions buffer too small".into()),
);
return Ok(RKRDB_ERR);
}
unsafe {
*out_natoms = n;
for (i, row) in pos.iter().enumerate() {
*out_xyz.add(i * 3) = row[0];
*out_xyz.add(i * 3 + 1) = row[1];
*out_xyz.add(i * 3 + 2) = row[2];
}
}
Ok(RKRDB_OK)
}
Err(e) => {
set_err(h, e);
Ok(RKRDB_ERR)
}
}
})
.unwrap_or(RKRDB_NULL)
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_get_forces(
id: usize,
traj_id: u64,
frame_idx: u32,
out_xyz: *mut f64,
capacity_atoms: u32,
out_natoms: *mut u32,
out_has_forces: *mut u8,
) -> c_int {
if out_xyz.is_null() || out_natoms.is_null() || out_has_forces.is_null() {
return RKRDB_NULL;
}
with_handle(id, |h| {
match h.corpus.get_forces(crate::keys::FrameKey {
traj_id,
frame_idx,
}) {
Ok(None) => {
unsafe {
*out_has_forces = 0;
*out_natoms = 0;
}
Ok(RKRDB_OK)
}
Ok(Some(frc)) => {
let n = frc.len() as u32;
if n > capacity_atoms {
set_err(
h,
crate::error::Error::Message("forces buffer too small".into()),
);
return Ok(RKRDB_ERR);
}
unsafe {
*out_has_forces = 1;
*out_natoms = n;
for (i, row) in frc.iter().enumerate() {
*out_xyz.add(i * 3) = row[0];
*out_xyz.add(i * 3 + 1) = row[1];
*out_xyz.add(i * 3 + 2) = row[2];
}
}
Ok(RKRDB_OK)
}
Err(e) => {
set_err(h, e);
Ok(RKRDB_ERR)
}
}
})
.unwrap_or(RKRDB_NULL)
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_recook_all(id: usize) -> c_int {
with_handle(id, |h| match h.corpus.recook_all() {
Ok(_) => Ok(RKRDB_OK),
Err(e) => {
set_err(h, e);
Ok(RKRDB_ERR)
}
})
.unwrap_or(RKRDB_NULL)
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_frame_formula(
id: usize,
traj_id: u64,
frame_idx: u32,
buf: *mut c_char,
buflen: usize,
) -> c_int {
if buf.is_null() || buflen == 0 {
return RKRDB_NULL;
}
with_handle(id, |h| {
match h.corpus.frame_formula(crate::keys::FrameKey {
traj_id,
frame_idx,
}) {
Ok(s) => {
let bytes = s.as_bytes();
if bytes.len() + 1 > buflen {
set_err(h, crate::error::Error::Message("buffer too small".into()));
return Ok(RKRDB_ERR);
}
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), buf as *mut u8, bytes.len());
*buf.add(bytes.len()) = 0;
}
Ok(RKRDB_OK)
}
Err(e) => {
set_err(h, e);
Ok(RKRDB_ERR)
}
}
})
.unwrap_or(RKRDB_NULL)
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_select_campaign(
id: usize,
traj_id: i64,
symbol: *const c_char,
natoms_min: u32,
natoms_max: u32,
formula: *const c_char,
energy_min: f64,
energy_max: f64,
use_energy_range: c_int,
fmax_min: f64,
fmax_max: f64,
use_fmax_range: c_int,
elem_sym: *const c_char,
elem_count: u32,
elem_exact: c_int,
flags: u32,
limit: u32,
) -> c_int {
with_handle(id, |h| {
let mut sel = Select::new().natoms_range(natoms_min, natoms_max);
if traj_id >= 0 {
sel = sel.trajectory(traj_id as u64);
}
if !symbol.is_null() {
let s = unsafe { CStr::from_ptr(symbol) };
if let Ok(sym) = s.to_str() {
if !sym.is_empty() {
sel = sel.require_symbol(sym);
}
}
}
if !formula.is_null() {
let s = unsafe { CStr::from_ptr(formula) };
if let Ok(f) = s.to_str() {
if !f.is_empty() {
sel = sel.exact_composition(f);
}
}
}
if use_energy_range != 0 {
sel = sel.energy_range(energy_min, energy_max);
}
if use_fmax_range != 0 {
sel = sel.fmax_range(fmax_min, fmax_max);
}
if !elem_sym.is_null() {
let s = unsafe { CStr::from_ptr(elem_sym) };
if let Ok(sym) = s.to_str() {
if !sym.is_empty() {
if elem_exact != 0 {
sel = sel.element_exact(sym, elem_count);
} else {
sel = sel.element_min(sym, elem_count);
}
}
}
}
if flags & 1 != 0 {
sel = sel.require_forces();
}
if flags & 2 != 0 {
sel = sel.require_velocities();
}
if flags & 4 != 0 {
sel = sel.require_energy();
}
if limit > 0 {
sel = sel.limit(limit as usize);
}
match h.corpus.select(&sel) {
Ok(keys) => {
h.last_keys = keys;
Ok(RKRDB_OK)
}
Err(e) => {
set_err(h, e);
Ok(RKRDB_ERR)
}
}
})
.unwrap_or(RKRDB_NULL)
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_result_count(id: usize) -> c_int {
with_handle(id, |h| Ok(h.last_keys.len() as c_int)).unwrap_or(RKRDB_NULL)
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_result_key(
id: usize,
i: usize,
out_traj: *mut u64,
out_frame: *mut u32,
) -> c_int {
if out_traj.is_null() || out_frame.is_null() {
return RKRDB_NULL;
}
with_handle(id, |h| {
let k = match h.last_keys.get(i) {
Some(k) => *k,
None => return Ok(RKRDB_NOT_FOUND),
};
unsafe {
*out_traj = k.traj_id;
*out_frame = k.frame_idx;
}
Ok(RKRDB_OK)
})
.unwrap_or(RKRDB_NULL)
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_frame_hash(
id: usize,
traj_id: u64,
frame_idx: u32,
out_hash16: *mut u8,
) -> c_int {
if out_hash16.is_null() {
return RKRDB_NULL;
}
let key = FrameKey {
traj_id,
frame_idx,
};
with_handle(id, |h| match h.corpus.frame_hash(key) {
Ok(hash) => {
let b = hash.to_bytes();
unsafe { ptr::copy_nonoverlapping(b.as_ptr(), out_hash16, 16) };
Ok(RKRDB_OK)
}
Err(e) => {
set_err(h, e);
Ok(RKRDB_ERR)
}
})
.unwrap_or(RKRDB_NULL)
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_get_frame_text(
id: usize,
traj_id: u64,
frame_idx: u32,
buf: *mut c_char,
buflen: usize,
) -> c_int {
if buf.is_null() || buflen == 0 {
return RKRDB_NULL;
}
let key = FrameKey {
traj_id,
frame_idx,
};
with_handle(id, |h| match h.corpus.get_frame_text(key) {
Ok(text) => {
let bytes = text.as_bytes();
if bytes.len() + 1 > buflen {
set_err(h, "buffer too small");
return Ok(RKRDB_ERR);
}
unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), buf as *mut u8, bytes.len());
*buf.add(bytes.len()) = 0;
}
Ok(bytes.len() as c_int)
}
Err(e) => {
set_err(h, e);
Ok(RKRDB_ERR)
}
})
.unwrap_or(RKRDB_NULL)
}
#[no_mangle]
pub unsafe extern "C" fn rkrdb_xxh3_128(data: *const u8, len: usize, out_hash16: *mut u8) -> c_int {
if data.is_null() || out_hash16.is_null() {
return RKRDB_NULL;
}
let slice = unsafe { std::slice::from_raw_parts(data, len) };
let h = hash_frame_bytes(slice);
let b = h.to_bytes();
unsafe { ptr::copy_nonoverlapping(b.as_ptr(), out_hash16, 16) };
RKRDB_OK
}
#[allow(dead_code)]
fn _cs(s: &str) -> Result<CString, c_int> {
CString::new(s).map_err(|_| RKRDB_ERR)
}
#[allow(dead_code)]
fn _ch(b: [u8; 16]) -> ContentHash {
ContentHash(b)
}