use std::ffi::CStr;
use std::os::raw::c_char;
use std::{ffi, slice, str};
use itertools::izip;
use crate::type_id::TypeId;
#[repr(C)]
pub struct TypeLut<'a> {
pub(crate) type_ids: *const TypeId<'a>,
pub(crate) type_handles: *mut *const ffi::c_void,
pub(crate) type_names: *const *const c_char,
pub num_entries: u32,
}
impl<'a> TypeLut<'a> {
pub fn iter(&self) -> impl Iterator<Item = (&TypeId, &*const ffi::c_void, &str)> {
let (type_ids, type_ptrs, type_names) = if self.num_entries != 0 {
let ptrs =
unsafe { slice::from_raw_parts_mut(self.type_handles, self.num_entries as usize) };
let type_ids =
unsafe { slice::from_raw_parts(self.type_ids, self.num_entries as usize) };
let type_names =
unsafe { slice::from_raw_parts(self.type_names, self.num_entries as usize) };
(type_ids.iter(), ptrs.iter(), type_names.iter())
} else {
(([]).iter(), ([]).iter(), ([]).iter())
};
izip!(type_ids, type_ptrs, type_names).map(|(id, ptr, type_name)| {
(id, ptr, unsafe {
std::str::from_utf8_unchecked(CStr::from_ptr(*type_name).to_bytes())
})
})
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&TypeId, &mut *const ffi::c_void, &str)> {
let (type_ids, type_ptrs, type_names) = if self.num_entries != 0 {
let ptrs =
unsafe { slice::from_raw_parts_mut(self.type_handles, self.num_entries as usize) };
let type_ids =
unsafe { slice::from_raw_parts(self.type_ids, self.num_entries as usize) };
let type_names =
unsafe { slice::from_raw_parts(self.type_names, self.num_entries as usize) };
(type_ids.iter(), ptrs.iter_mut(), type_names.iter())
} else {
(([]).iter(), ([]).iter_mut(), ([]).iter())
};
izip!(type_ids, type_ptrs, type_names).map(|(id, ptr, type_name)| {
(id, ptr, unsafe {
std::str::from_utf8_unchecked(CStr::from_ptr(*type_name).to_bytes())
})
})
}
pub fn type_handles_mut(&mut self) -> &mut [*const ffi::c_void] {
if self.num_entries == 0 {
&mut []
} else {
unsafe { slice::from_raw_parts_mut(self.type_handles, self.num_entries as usize) }
}
}
pub fn type_ids(&self) -> &[TypeId<'a>] {
if self.num_entries == 0 {
&[]
} else {
unsafe { slice::from_raw_parts(self.type_ids, self.num_entries as usize) }
}
}
pub unsafe fn get_type_handle_unchecked(&self, idx: u32) -> *const ffi::c_void {
*self.type_handles.offset(idx as isize)
}
pub fn get_type_handle(&self, idx: u32) -> Option<*const ffi::c_void> {
if idx < self.num_entries {
Some(unsafe { self.get_type_handle_unchecked(idx) })
} else {
None
}
}
pub unsafe fn get_type_handle_unchecked_mut(&mut self, idx: u32) -> &mut *const ffi::c_void {
&mut *self.type_handles.offset(idx as isize)
}
pub fn get_type_handle_mut(&mut self, idx: u32) -> Option<&mut *const ffi::c_void> {
if idx < self.num_entries {
Some(unsafe { self.get_type_handle_unchecked_mut(idx) })
} else {
None
}
}
pub fn type_names(&self) -> impl Iterator<Item = &str> {
let type_names = if self.num_entries == 0 {
&[]
} else {
unsafe { slice::from_raw_parts(self.type_names, self.num_entries as usize) }
};
type_names
.iter()
.map(|n| unsafe { str::from_utf8_unchecked(CStr::from_ptr(*n).to_bytes()) })
}
}
#[cfg(feature = "serde")]
impl<'a> serde::Serialize for TypeLut<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeSeq;
let mut s = serializer.serialize_seq(Some(self.num_entries as usize))?;
for (ty, _, name) in self.iter() {
#[derive(serde::Serialize)]
struct Elem<'a> {
name: &'a str,
r#type: &'a TypeId<'a>,
}
s.serialize_element(&Elem { name, r#type: ty })?;
}
s.end()
}
}
#[cfg(test)]
mod tests {
use std::{ffi::CString, ptr};
use crate::test_utils::{fake_type_lut, FAKE_TYPE_ID, FAKE_TYPE_NAME};
#[test]
fn test_type_lut_iter_mut_none() {
let type_ids = &[];
let type_ptrs = &mut [];
let type_names = &[];
let mut type_lut = fake_type_lut(type_ids, type_ptrs, type_names);
let iter = type_ids.iter().zip(type_ptrs.iter_mut());
assert_eq!(type_lut.iter_mut().count(), iter.count());
}
#[test]
fn test_type_lut_iter_mut_some() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let type_ids = &[FAKE_TYPE_ID];
let type_ptrs = &mut [ptr::null()];
let type_names = &[type_name.as_ptr()];
let mut type_lut = fake_type_lut(type_ids, type_ptrs, type_names);
let iter = type_ids.iter().zip(type_ptrs.iter_mut());
assert_eq!(type_lut.iter_mut().count(), iter.len());
for (lhs, rhs) in type_lut.iter_mut().zip(iter) {
assert_eq!(lhs.0, rhs.0);
assert_eq!(lhs.1, rhs.1);
}
}
#[test]
fn test_type_lut_iter_none() {
let type_ids = &[];
let type_ptrs = &mut [];
let type_names = &[];
let type_lut = fake_type_lut(type_ids, type_ptrs, type_names);
let iter = type_ids.iter().zip(type_ptrs.iter_mut());
assert_eq!(type_lut.iter().count(), iter.count());
}
#[test]
fn test_type_lut_iter_some() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let type_ids = &[FAKE_TYPE_ID];
let type_ptrs = &mut [ptr::null()];
let type_names = &[type_name.as_ptr()];
let type_lut = fake_type_lut(type_ids, type_ptrs, type_names);
let iter = type_ids.iter().zip(type_ptrs.iter_mut());
assert_eq!(type_lut.iter().count(), iter.len());
for (lhs, rhs) in type_lut.iter().zip(iter) {
assert_eq!(lhs.0, rhs.0);
assert_eq!(lhs.1, rhs.1);
}
}
#[test]
fn test_type_lut_ptrs_mut_none() {
let type_ids = &[];
let type_ptrs = &mut [];
let type_names = &[];
let mut type_lut = fake_type_lut(type_ids, type_ptrs, type_names);
assert_eq!(type_lut.type_handles_mut().len(), 0);
}
#[test]
fn test_type_lut_ptrs_mut_some() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let type_ids = &[FAKE_TYPE_ID];
let type_ptrs = &mut [ptr::null()];
let type_names = &[type_name.as_ptr()];
let mut type_lut = fake_type_lut(type_ids, type_ptrs, type_names);
let result = type_lut.type_handles_mut();
assert_eq!(result.len(), type_ptrs.len());
for (lhs, rhs) in result.iter().zip(type_ptrs.iter()) {
assert_eq!(lhs, rhs);
}
}
#[test]
fn test_type_lut_type_ids_none() {
let type_ids = &[];
let type_ptrs = &mut [];
let type_names = &[];
let type_lut = fake_type_lut(type_ids, type_ptrs, type_names);
assert_eq!(type_lut.type_ids().len(), 0);
}
#[test]
fn test_type_lut_type_ids_some() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let type_ids = &[FAKE_TYPE_ID];
let type_ptrs = &mut [ptr::null()];
let type_names = &[type_name.as_ptr()];
let type_lut = fake_type_lut(type_ids, type_ptrs, type_names);
let result = type_lut.type_ids();
assert_eq!(result.len(), type_ids.len());
for (lhs, rhs) in result.iter().zip(type_ids.iter()) {
assert_eq!(lhs, rhs);
}
}
#[test]
fn test_type_lut_get_ptr_unchecked() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let type_ids = &[FAKE_TYPE_ID];
let type_ptrs = &mut [ptr::null()];
let type_names = &[type_name.as_ptr()];
let type_lut = fake_type_lut(type_ids, type_ptrs, type_names);
assert_eq!(
unsafe { type_lut.get_type_handle_unchecked(0) },
type_ptrs[0]
);
}
#[test]
fn test_type_lut_get_ptr_none() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let prototype = &[FAKE_TYPE_ID];
let type_ptrs = &mut [ptr::null()];
let type_names = &[type_name.as_ptr()];
let type_lut = fake_type_lut(prototype, type_ptrs, type_names);
assert_eq!(type_lut.get_type_handle(1), None);
}
#[test]
fn test_type_lut_get_ptr_some() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let type_ids = &[FAKE_TYPE_ID];
let type_ptrs = &mut [ptr::null()];
let type_names = &[type_name.as_ptr()];
let type_lut = fake_type_lut(type_ids, type_ptrs, type_names);
assert_eq!(type_lut.get_type_handle(0), Some(type_ptrs[0]));
}
#[test]
fn test_type_lut_get_ptr_unchecked_mut() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let type_ids = &[FAKE_TYPE_ID];
let type_ptrs = &mut [ptr::null()];
let type_names = &[type_name.as_ptr()];
let mut type_lut = fake_type_lut(type_ids, type_ptrs, type_names);
assert_eq!(
unsafe { type_lut.get_type_handle_unchecked_mut(0) },
&mut type_ptrs[0]
);
}
#[test]
fn test_type_lut_get_ptr_mut_none() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let type_ids = &[FAKE_TYPE_ID];
let type_ptrs = &mut [ptr::null()];
let type_names = &[type_name.as_ptr()];
let mut type_lut = fake_type_lut(type_ids, type_ptrs, type_names);
assert_eq!(type_lut.get_type_handle_mut(1), None);
}
#[test]
fn test_type_lut_get_ptr_mut_some() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let type_ids = &[FAKE_TYPE_ID];
let type_ptrs = &mut [ptr::null()];
let type_names = &[type_name.as_ptr()];
let mut type_lut = fake_type_lut(type_ids, type_ptrs, type_names);
assert_eq!(type_lut.get_type_handle_mut(0), Some(&mut type_ptrs[0]));
}
#[test]
fn test_type_lut_type_names_none() {
let type_ids = &[];
let type_ptrs = &mut [];
let type_names = &[];
let type_lut = fake_type_lut(type_ids, type_ptrs, type_names);
assert_eq!(type_lut.type_names().count(), 0);
}
#[test]
fn test_type_lut_type_names_some() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let type_ids = &[FAKE_TYPE_ID];
let type_ptrs = &mut [ptr::null()];
let type_names = &[type_name.as_ptr()];
let type_lut = fake_type_lut(type_ids, type_ptrs, type_names);
for (lhs, rhs) in type_lut.type_names().zip([FAKE_TYPE_NAME].iter()) {
assert_eq!(lhs, *rhs)
}
}
}