#![allow(clippy::not_unsafe_ptr_arg_deref)]
use crate::davo::freshness_v2::FreshnessIndexV2;
use crate::davo::predictor::DecayPredictor;
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use std::collections::HashMap;
use std::ffi::{c_char, c_double, c_int, CStr};
use std::ptr;
pub const DAVO_SUCCESS: c_int = 1;
pub const DAVO_ERR_NULL_PTR: c_int = -1;
pub const DAVO_ERR_INVALID_UTF8: c_int = -2;
pub const DAVO_ERR_NOT_FOUND: c_int = -3;
pub const DAVO_ERR_ALREADY_EXISTS: c_int = -4;
pub const DAVO_ERR_INTERNAL: c_int = -100;
static FRESHNESS_REGISTRY: Lazy<Mutex<HashMap<String, FreshnessIndexV2>>> =
Lazy::new(|| Mutex::new(HashMap::new()));
static PREDICTOR_REGISTRY: Lazy<Mutex<HashMap<String, DecayPredictor>>> =
Lazy::new(|| Mutex::new(HashMap::new()));
unsafe fn cstr_to_str(ptr: *const c_char) -> Option<&'static str> {
if ptr.is_null() {
return None;
}
CStr::from_ptr(ptr).to_str().ok()
}
#[no_mangle]
pub extern "C" fn SYNA_davo_freshness_index_new(path: *const c_char, threshold: c_double) -> c_int {
std::panic::catch_unwind(|| {
let path = match unsafe { cstr_to_str(path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
let mut registry = FRESHNESS_REGISTRY.lock();
if registry.contains_key(path) {
return DAVO_ERR_ALREADY_EXISTS;
}
let index = if threshold > 0.0 && threshold < 1.0 {
FreshnessIndexV2::with_threshold(threshold as f32)
} else {
FreshnessIndexV2::new()
};
registry.insert(path.to_string(), index);
DAVO_SUCCESS
})
.unwrap_or(DAVO_ERR_INTERNAL)
}
#[no_mangle]
pub extern "C" fn SYNA_davo_freshness_index_insert(
path: *const c_char,
key: *const c_char,
decay_rate: c_double,
) -> c_int {
std::panic::catch_unwind(|| {
let path = match unsafe { cstr_to_str(path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
let key = match unsafe { cstr_to_str(key) } {
Some(k) => k,
None => return DAVO_ERR_NULL_PTR,
};
let mut registry = FRESHNESS_REGISTRY.lock();
let index = match registry.get_mut(path) {
Some(i) => i,
None => return DAVO_ERR_NOT_FOUND,
};
index.insert(key, decay_rate as f32);
DAVO_SUCCESS
})
.unwrap_or(DAVO_ERR_INTERNAL)
}
#[no_mangle]
pub extern "C" fn SYNA_davo_freshness_index_get_freshness(
path: *const c_char,
key: *const c_char,
out_freshness: *mut c_double,
) -> c_int {
std::panic::catch_unwind(|| {
let path = match unsafe { cstr_to_str(path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
let key = match unsafe { cstr_to_str(key) } {
Some(k) => k,
None => return DAVO_ERR_NULL_PTR,
};
if out_freshness.is_null() {
return DAVO_ERR_NULL_PTR;
}
let registry = FRESHNESS_REGISTRY.lock();
let index = match registry.get(path) {
Some(i) => i,
None => return DAVO_ERR_NOT_FOUND,
};
match index.get_freshness(key) {
Some(f) => {
unsafe { *out_freshness = f as c_double };
DAVO_SUCCESS
}
None => DAVO_ERR_NOT_FOUND,
}
})
.unwrap_or(DAVO_ERR_INTERNAL)
}
#[no_mangle]
pub extern "C" fn SYNA_davo_freshness_index_query_stale(
path: *const c_char,
out_keys: *mut *mut *mut c_char,
out_count: *mut usize,
) -> c_int {
std::panic::catch_unwind(|| {
let path = match unsafe { cstr_to_str(path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
if out_keys.is_null() || out_count.is_null() {
return DAVO_ERR_NULL_PTR;
}
let registry = FRESHNESS_REGISTRY.lock();
let index = match registry.get(path) {
Some(i) => i,
None => return DAVO_ERR_NOT_FOUND,
};
let stale = index.query_stale();
let count = stale.len();
let array = if count > 0 {
let layout = std::alloc::Layout::array::<*mut c_char>(count)
.unwrap_or(std::alloc::Layout::new::<*mut c_char>());
let ptr = unsafe { std::alloc::alloc(layout) } as *mut *mut c_char;
if ptr.is_null() {
return DAVO_ERR_INTERNAL;
}
for (i, key) in stale.iter().enumerate() {
let cstring = match std::ffi::CString::new(key.as_str()) {
Ok(c) => c,
Err(_) => {
for j in 0..i {
unsafe {
let _ = std::ffi::CString::from_raw(*ptr.add(j));
}
}
unsafe { std::alloc::dealloc(ptr as *mut u8, layout) };
return DAVO_ERR_INTERNAL;
}
};
unsafe { *ptr.add(i) = cstring.into_raw() };
}
ptr
} else {
ptr::null_mut()
};
unsafe {
*out_keys = array;
*out_count = count;
}
DAVO_SUCCESS
})
.unwrap_or(DAVO_ERR_INTERNAL)
}
#[no_mangle]
pub extern "C" fn SYNA_davo_freshness_index_evict_stale(
path: *const c_char,
out_count: *mut usize,
) -> c_int {
std::panic::catch_unwind(|| {
let path = match unsafe { cstr_to_str(path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
if out_count.is_null() {
return DAVO_ERR_NULL_PTR;
}
let mut registry = FRESHNESS_REGISTRY.lock();
let index = match registry.get_mut(path) {
Some(i) => i,
None => return DAVO_ERR_NOT_FOUND,
};
let evicted = index.evict_stale();
unsafe { *out_count = evicted.len() };
DAVO_SUCCESS
})
.unwrap_or(DAVO_ERR_INTERNAL)
}
#[no_mangle]
pub extern "C" fn SYNA_davo_freshness_index_len(path: *const c_char) -> i64 {
std::panic::catch_unwind(|| {
let path = match unsafe { cstr_to_str(path) } {
Some(p) => p,
None => return -1_i64,
};
let registry = FRESHNESS_REGISTRY.lock();
match registry.get(path) {
Some(i) => i.len() as i64,
None => -1,
}
})
.unwrap_or(-1)
}
#[no_mangle]
pub extern "C" fn SYNA_davo_freshness_index_close(path: *const c_char) -> c_int {
std::panic::catch_unwind(|| {
let path = match unsafe { cstr_to_str(path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
let mut registry = FRESHNESS_REGISTRY.lock();
if registry.remove(path).is_some() {
DAVO_SUCCESS
} else {
DAVO_ERR_NOT_FOUND
}
})
.unwrap_or(DAVO_ERR_INTERNAL)
}
#[no_mangle]
pub extern "C" fn SYNA_davo_freshness_index_save(
path: *const c_char,
file_path: *const c_char,
) -> c_int {
std::panic::catch_unwind(|| {
let path = match unsafe { cstr_to_str(path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
let file_path = match unsafe { cstr_to_str(file_path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
let registry = FRESHNESS_REGISTRY.lock();
let index = match registry.get(path) {
Some(i) => i,
None => return DAVO_ERR_NOT_FOUND,
};
match index.save(file_path) {
Ok(_) => DAVO_SUCCESS,
Err(_) => DAVO_ERR_INTERNAL,
}
})
.unwrap_or(DAVO_ERR_INTERNAL)
}
#[no_mangle]
pub extern "C" fn SYNA_davo_freshness_index_load(
path: *const c_char,
file_path: *const c_char,
) -> c_int {
std::panic::catch_unwind(|| {
let path = match unsafe { cstr_to_str(path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
let file_path = match unsafe { cstr_to_str(file_path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
let mut registry = FRESHNESS_REGISTRY.lock();
if registry.contains_key(path) {
return DAVO_ERR_ALREADY_EXISTS;
}
match FreshnessIndexV2::load(file_path) {
Ok(index) => {
registry.insert(path.to_string(), index);
DAVO_SUCCESS
}
Err(_) => DAVO_ERR_NOT_FOUND,
}
})
.unwrap_or(DAVO_ERR_INTERNAL)
}
#[no_mangle]
pub extern "C" fn SYNA_davo_free_keys(keys: *mut *mut c_char, count: usize) {
if keys.is_null() || count == 0 {
return;
}
for i in 0..count {
let key_ptr = unsafe { *keys.add(i) };
if !key_ptr.is_null() {
unsafe {
let _ = std::ffi::CString::from_raw(key_ptr);
}
}
}
if let Ok(layout) = std::alloc::Layout::array::<*mut c_char>(count) {
unsafe { std::alloc::dealloc(keys as *mut u8, layout) };
}
}
#[no_mangle]
pub extern "C" fn SYNA_davo_predictor_new(path: *const c_char) -> c_int {
std::panic::catch_unwind(|| {
let path = match unsafe { cstr_to_str(path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
let mut registry = PREDICTOR_REGISTRY.lock();
if registry.contains_key(path) {
return DAVO_ERR_ALREADY_EXISTS;
}
registry.insert(path.to_string(), DecayPredictor::new());
DAVO_SUCCESS
})
.unwrap_or(DAVO_ERR_INTERNAL)
}
#[no_mangle]
pub extern "C" fn SYNA_davo_predictor_observe(
path: *const c_char,
actual_decay: c_double,
) -> c_int {
std::panic::catch_unwind(|| {
let path = match unsafe { cstr_to_str(path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
let mut registry = PREDICTOR_REGISTRY.lock();
let predictor = match registry.get_mut(path) {
Some(p) => p,
None => return DAVO_ERR_NOT_FOUND,
};
predictor.observe(actual_decay as f32);
DAVO_SUCCESS
})
.unwrap_or(DAVO_ERR_INTERNAL)
}
#[no_mangle]
pub extern "C" fn SYNA_davo_predictor_predict(
path: *const c_char,
out_prediction: *mut c_double,
) -> c_int {
std::panic::catch_unwind(|| {
let path = match unsafe { cstr_to_str(path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
if out_prediction.is_null() {
return DAVO_ERR_NULL_PTR;
}
let registry = PREDICTOR_REGISTRY.lock();
let predictor = match registry.get(path) {
Some(p) => p,
None => return DAVO_ERR_NOT_FOUND,
};
unsafe { *out_prediction = predictor.predict() as c_double };
DAVO_SUCCESS
})
.unwrap_or(DAVO_ERR_INTERNAL)
}
#[no_mangle]
pub extern "C" fn SYNA_davo_predictor_sample(
path: *const c_char,
out_sample: *mut c_double,
) -> c_int {
std::panic::catch_unwind(|| {
let path = match unsafe { cstr_to_str(path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
if out_sample.is_null() {
return DAVO_ERR_NULL_PTR;
}
let mut registry = PREDICTOR_REGISTRY.lock();
let predictor = match registry.get_mut(path) {
Some(p) => p,
None => return DAVO_ERR_NOT_FOUND,
};
unsafe { *out_sample = predictor.sample() as c_double };
DAVO_SUCCESS
})
.unwrap_or(DAVO_ERR_INTERNAL)
}
#[no_mangle]
pub extern "C" fn SYNA_davo_predictor_uncertainty(
path: *const c_char,
out_uncertainty: *mut c_double,
) -> c_int {
std::panic::catch_unwind(|| {
let path = match unsafe { cstr_to_str(path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
if out_uncertainty.is_null() {
return DAVO_ERR_NULL_PTR;
}
let registry = PREDICTOR_REGISTRY.lock();
let predictor = match registry.get(path) {
Some(p) => p,
None => return DAVO_ERR_NOT_FOUND,
};
unsafe { *out_uncertainty = predictor.uncertainty() as c_double };
DAVO_SUCCESS
})
.unwrap_or(DAVO_ERR_INTERNAL)
}
#[no_mangle]
pub extern "C" fn SYNA_davo_predictor_close(path: *const c_char) -> c_int {
std::panic::catch_unwind(|| {
let path = match unsafe { cstr_to_str(path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
let mut registry = PREDICTOR_REGISTRY.lock();
if registry.remove(path).is_some() {
DAVO_SUCCESS
} else {
DAVO_ERR_NOT_FOUND
}
})
.unwrap_or(DAVO_ERR_INTERNAL)
}
#[no_mangle]
pub extern "C" fn SYNA_davo_predictor_save(path: *const c_char, file_path: *const c_char) -> c_int {
std::panic::catch_unwind(|| {
let path = match unsafe { cstr_to_str(path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
let file_path = match unsafe { cstr_to_str(file_path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
let registry = PREDICTOR_REGISTRY.lock();
let predictor = match registry.get(path) {
Some(p) => p,
None => return DAVO_ERR_NOT_FOUND,
};
match predictor.save(file_path) {
Ok(_) => DAVO_SUCCESS,
Err(_) => DAVO_ERR_INTERNAL,
}
})
.unwrap_or(DAVO_ERR_INTERNAL)
}
#[no_mangle]
pub extern "C" fn SYNA_davo_predictor_load(path: *const c_char, file_path: *const c_char) -> c_int {
std::panic::catch_unwind(|| {
let path = match unsafe { cstr_to_str(path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
let file_path = match unsafe { cstr_to_str(file_path) } {
Some(p) => p,
None => return DAVO_ERR_NULL_PTR,
};
let mut registry = PREDICTOR_REGISTRY.lock();
if registry.contains_key(path) {
return DAVO_ERR_ALREADY_EXISTS;
}
match DecayPredictor::load(file_path) {
Ok(predictor) => {
registry.insert(path.to_string(), predictor);
DAVO_SUCCESS
}
Err(_) => DAVO_ERR_NOT_FOUND,
}
})
.unwrap_or(DAVO_ERR_INTERNAL)
}