hakuban 0.8.5

Data-object sharing library
Documentation
#![allow(clippy::not_unsafe_ptr_arg_deref)]

use std::{
	ffi::{c_void, CString},
	mem::forget,
	sync::Arc,
};

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<(Box<[CString]>, Box<[*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 {
	//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(std::time::Instant::now() - std::time::Duration::from_micros(microseconds)),
	};

	let object_state = ObjectState { data: serialized_data, version, format: data_format, synchronized };
	FFIResult::pointer(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: Box<[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()))
}