use crate::FunctionPrototype;
use std::{ffi::c_void, slice};
#[repr(C)]
pub struct DispatchTable<'a> {
pub(crate) prototypes: *const FunctionPrototype<'a>,
pub(crate) fn_ptrs: *mut *const c_void,
pub num_entries: u32,
}
impl<'a> DispatchTable<'a> {
pub fn iter_mut(
&mut self,
) -> impl Iterator<Item = (&mut *const c_void, &FunctionPrototype<'a>)> {
if self.num_entries == 0 {
([]).iter_mut().zip(([]).iter())
} else {
let ptrs =
unsafe { slice::from_raw_parts_mut(self.fn_ptrs, self.num_entries as usize) };
let signatures =
unsafe { slice::from_raw_parts(self.prototypes, self.num_entries as usize) };
ptrs.iter_mut().zip(signatures.iter())
}
}
pub fn iter(&self) -> impl Iterator<Item = (&*const c_void, &FunctionPrototype<'a>)> {
if self.num_entries == 0 {
([]).iter().zip(([]).iter())
} else {
let ptrs =
unsafe { slice::from_raw_parts_mut(self.fn_ptrs, self.num_entries as usize) };
let signatures =
unsafe { slice::from_raw_parts(self.prototypes, self.num_entries as usize) };
ptrs.iter().zip(signatures.iter())
}
}
pub fn ptrs_mut(&mut self) -> &mut [*const c_void] {
if self.num_entries == 0 {
&mut []
} else {
unsafe { slice::from_raw_parts_mut(self.fn_ptrs, self.num_entries as usize) }
}
}
pub fn prototypes(&self) -> &[FunctionPrototype<'a>] {
if self.num_entries == 0 {
&[]
} else {
unsafe { slice::from_raw_parts(self.prototypes, self.num_entries as usize) }
}
}
pub unsafe fn get_ptr_unchecked(&self, idx: u32) -> *const c_void {
*self.fn_ptrs.offset(idx as isize)
}
pub fn get_ptr(&self, idx: u32) -> Option<*const c_void> {
if idx < self.num_entries {
Some(unsafe { self.get_ptr_unchecked(idx) })
} else {
None
}
}
pub unsafe fn get_ptr_unchecked_mut(&mut self, idx: u32) -> &mut *const c_void {
&mut *self.fn_ptrs.offset(idx as isize)
}
pub fn get_ptr_mut(&mut self, idx: u32) -> Option<&mut *const c_void> {
if idx < self.num_entries {
Some(unsafe { self.get_ptr_unchecked_mut(idx) })
} else {
None
}
}
}
#[cfg(feature = "serde")]
impl<'a> serde::Serialize for DispatchTable<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut s = serializer.serialize_struct("DispatchTable", 1)?;
s.serialize_field("prototypes", self.prototypes())?;
s.end()
}
}
#[cfg(test)]
mod tests {
use crate::test_utils::{fake_dispatch_table, fake_fn_prototype, FAKE_FN_NAME};
use crate::type_id::HasStaticTypeId;
use std::{ffi::CString, ptr};
#[test]
fn test_dispatch_table_iter_mut_none() {
let signatures = &[];
let fn_ptrs = &mut [];
let mut dispatch_table = fake_dispatch_table(signatures, fn_ptrs);
let iter = fn_ptrs.iter_mut().zip(signatures.iter());
assert_eq!(dispatch_table.iter_mut().count(), iter.count());
}
#[test]
fn test_dispatch_table_iter_mut_some() {
let type_id = i32::type_id().clone();
let return_type = Some(type_id);
let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name.");
let fn_prototype = fake_fn_prototype(&fn_name, &[], return_type);
let prototypes = &[fn_prototype];
let fn_ptrs = &mut [ptr::null()];
let mut dispatch_table = fake_dispatch_table(prototypes, fn_ptrs);
let iter = fn_ptrs.iter_mut().zip(prototypes.iter());
assert_eq!(dispatch_table.iter_mut().count(), iter.len());
for (lhs, rhs) in dispatch_table.iter_mut().zip(iter) {
assert_eq!(lhs.0, rhs.0);
assert_eq!(lhs.1.name(), rhs.1.name());
assert_eq!(lhs.1.signature.arg_types(), rhs.1.signature.arg_types());
assert_eq!(lhs.1.signature.return_type, rhs.1.signature.return_type);
}
}
#[test]
fn test_dispatch_table_ptrs_mut_none() {
let signatures = &[];
let fn_ptrs = &mut [];
let mut dispatch_table = fake_dispatch_table(signatures, fn_ptrs);
assert_eq!(dispatch_table.ptrs_mut().len(), 0);
}
#[test]
fn test_dispatch_table_ptrs_mut_some() {
let type_id = i32::type_id().clone();
let return_type = Some(type_id);
let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name.");
let fn_prototype = fake_fn_prototype(&fn_name, &[], return_type);
let prototypes = &[fn_prototype];
let fn_ptrs = &mut [ptr::null()];
let mut dispatch_table = fake_dispatch_table(prototypes, fn_ptrs);
let result = dispatch_table.ptrs_mut();
assert_eq!(result.len(), fn_ptrs.len());
for (lhs, rhs) in result.iter().zip(fn_ptrs.iter()) {
assert_eq!(lhs, rhs);
}
}
#[test]
fn test_dispatch_table_signatures_none() {
let signatures = &[];
let fn_ptrs = &mut [];
let dispatch_table = fake_dispatch_table(signatures, fn_ptrs);
assert_eq!(dispatch_table.prototypes().len(), 0);
}
#[test]
fn test_dispatch_table_signatures_some() {
let type_id = i32::type_id().clone();
let return_type = Some(type_id);
let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name.");
let fn_prototype = fake_fn_prototype(&fn_name, &[], return_type);
let prototypes = &[fn_prototype];
let fn_ptrs = &mut [ptr::null()];
let dispatch_table = fake_dispatch_table(prototypes, fn_ptrs);
let result = dispatch_table.prototypes();
assert_eq!(result.len(), prototypes.len());
for (lhs, rhs) in result.iter().zip(prototypes.iter()) {
assert_eq!(lhs.name(), rhs.name());
assert_eq!(lhs.signature.arg_types(), rhs.signature.arg_types());
assert_eq!(lhs.signature.return_type, rhs.signature.return_type);
}
}
#[test]
fn test_dispatch_table_get_ptr_unchecked() {
let type_id = i32::type_id();
let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name.");
let fn_prototype = fake_fn_prototype(&fn_name, &[], Some(type_id.clone()));
let prototypes = &[fn_prototype];
let fn_ptrs = &mut [ptr::null()];
let dispatch_table = fake_dispatch_table(prototypes, fn_ptrs);
assert_eq!(unsafe { dispatch_table.get_ptr_unchecked(0) }, fn_ptrs[0]);
}
#[test]
fn test_dispatch_table_get_ptr_none() {
let type_id = i32::type_id();
let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name.");
let fn_prototype = fake_fn_prototype(&fn_name, &[], Some(type_id.clone()));
let prototype = &[fn_prototype];
let fn_ptrs = &mut [ptr::null()];
let dispatch_table = fake_dispatch_table(prototype, fn_ptrs);
assert_eq!(dispatch_table.get_ptr(1), None);
}
#[test]
fn test_dispatch_table_get_ptr_some() {
let type_id = i32::type_id();
let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name.");
let fn_prototype = fake_fn_prototype(&fn_name, &[], Some(type_id.clone()));
let prototypes = &[fn_prototype];
let fn_ptrs = &mut [ptr::null()];
let dispatch_table = fake_dispatch_table(prototypes, fn_ptrs);
assert_eq!(dispatch_table.get_ptr(0), Some(fn_ptrs[0]));
}
#[test]
fn test_dispatch_table_get_ptr_unchecked_mut() {
let type_id = i32::type_id();
let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name.");
let fn_prototype = fake_fn_prototype(&fn_name, &[], Some(type_id.clone()));
let prototypes = &[fn_prototype];
let fn_ptrs = &mut [ptr::null()];
let mut dispatch_table = fake_dispatch_table(prototypes, fn_ptrs);
assert_eq!(
unsafe { dispatch_table.get_ptr_unchecked_mut(0) },
&mut fn_ptrs[0]
);
}
#[test]
fn test_dispatch_table_get_ptr_mut_none() {
let type_id = i32::type_id();
let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name.");
let fn_prototype = fake_fn_prototype(&fn_name, &[], Some(type_id.clone()));
let prototypes = &[fn_prototype];
let fn_ptrs = &mut [ptr::null()];
let mut dispatch_table = fake_dispatch_table(prototypes, fn_ptrs);
assert_eq!(dispatch_table.get_ptr_mut(1), None);
}
#[test]
fn test_dispatch_table_get_ptr_mut_some() {
let type_id = i32::type_id();
let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name.");
let fn_prototype = fake_fn_prototype(&fn_name, &[], Some(type_id.clone()));
let prototypes = &[fn_prototype];
let fn_ptrs = &mut [ptr::null()];
let mut dispatch_table = fake_dispatch_table(prototypes, fn_ptrs);
assert_eq!(dispatch_table.get_ptr_mut(0), Some(&mut fn_ptrs[0]));
}
}