hakuban 0.7.2

Data-object sharing library
Documentation
#![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> {
	//TODO: these should be slices?
	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()))
}