#![allow(clippy::not_unsafe_ptr_arg_deref)]
use std::{ffi::{c_void, CString}, mem::forget, sync::Arc};
use instant::Instant;
use log::error;
use super::{FFIBorrowedArray, FFIResult};
use crate::{ffi::FFIResultStatus, object::ObjectState, DataBytes, DataSynchronized};
pub struct FFIObjectState {
pub(super) object_state: ObjectState<DataBytes>,
data_format_c_strings: Option<(Vec<CString>, Vec<*const i8>)>,
}
impl FFIObjectState {
pub(super) fn new(object_state: ObjectState<DataBytes>) -> FFIObjectState {
FFIObjectState { object_state, data_format_c_strings: None }
}
}
impl Clone for FFIObjectState {
fn clone(&self) -> Self {
Self { object_state: self.object_state.clone(), data_format_c_strings: None }
}
}
#[no_mangle]
pub extern "C" fn hakuban_object_state_new(
version_length: usize, version_elements: *mut i64, data_format_length: usize, data_format_c_strings: *const *const i8, data_length: usize, data: *mut u8,
synchronized_us_ago: u64,
) -> FFIResult<*mut FFIObjectState> {
let version_submitted = unsafe { Vec::from_raw_parts(version_elements, version_length, version_length) };
let version = version_submitted.clone();
forget(version_submitted);
let data_format_slice: &[*const i8] = unsafe { std::slice::from_raw_parts(data_format_c_strings, data_format_length) };
let data_format: Result<Vec<String>, std::str::Utf8Error> =
data_format_slice.iter().map(|c_str| unsafe { Ok(std::ffi::CStr::from_ptr(*c_str).to_str()?.to_owned()) }).collect();
let data_format = match data_format {
Ok(strings) => strings,
Err(error) => {
error!("Couldn't process data_format field: {:?}", error);
return FFIResult::error(FFIResultStatus::InvalidString);
}
};
let data_submitted = unsafe { Vec::from_raw_parts(data, data_length, data_length) };
let serialized_data = Arc::new(data_submitted.clone());
forget(data_submitted);
let synchronized = match synchronized_us_ago {
0 => DataSynchronized::Now,
microseconds => DataSynchronized::LastAt(Instant::now() - std::time::Duration::from_micros(microseconds)),
};
let object_state = ObjectState { data: serialized_data, version, format: data_format, synchronized };
FFIResult::ok(Box::into_raw(Box::new(FFIObjectState { object_state, data_format_c_strings: None })))
}
#[no_mangle]
pub extern "C" fn hakuban_object_state_drop(object_state_pointer: *mut FFIObjectState) {
drop(unsafe { Box::from_raw(object_state_pointer) });
}
#[no_mangle]
pub extern "C" fn hakuban_object_state_data(object_state_pointer: *mut FFIObjectState) -> FFIBorrowedArray {
let object_state: &mut FFIObjectState = unsafe { object_state_pointer.as_mut().unwrap() };
FFIBorrowedArray { length: object_state.object_state.data.len(), pointer: object_state.object_state.data.as_ptr() as *mut c_void }
}
#[no_mangle]
pub extern "C" fn hakuban_object_state_format(object_state_pointer: *mut FFIObjectState) -> FFIBorrowedArray {
let object_state: &mut FFIObjectState = unsafe { object_state_pointer.as_mut().unwrap() };
let (_, array) = object_state.data_format_c_strings.get_or_insert_with(|| {
let strings: Vec<CString> = object_state.object_state.format.iter().map(|rust_string| CString::new(rust_string.clone()).unwrap()).collect();
let array = strings.iter().map(|cstring| cstring.as_ptr()).collect();
(strings, array)
});
FFIBorrowedArray { length: object_state.object_state.format.len(), pointer: array.as_ptr() as *mut c_void }
}
#[no_mangle]
pub extern "C" fn hakuban_object_state_version(object_state_pointer: *mut FFIObjectState) -> FFIBorrowedArray {
let object_state: &mut FFIObjectState = unsafe { object_state_pointer.as_mut().unwrap() };
FFIBorrowedArray { length: object_state.object_state.version.len(), pointer: object_state.object_state.version.as_ptr() as *mut c_void }
}
#[no_mangle]
pub extern "C" fn hakuban_object_state_synchronized_ago(object_state_pointer: *mut FFIObjectState) -> u64 {
let object_state: &mut FFIObjectState = unsafe { object_state_pointer.as_mut().unwrap() };
object_state.object_state.synchronized.micros_ago()
}
#[no_mangle]
pub extern "C" fn hakuban_object_state_clone(object_state_pointer: *mut FFIObjectState) -> *mut FFIObjectState {
let object_state: &mut FFIObjectState = unsafe { object_state_pointer.as_mut().unwrap() };
Box::into_raw(Box::new(object_state.clone()))
}