use std::{
any::{Any, TypeId},
cmp::Ordering,
mem::ManuallyDrop,
sync::OnceLock,
};
use crate::entry::{BenchEntryRunner, GroupEntry};
pub struct GenericBenchEntry {
pub group: &'static GroupEntry,
pub bench: BenchEntryRunner,
pub ty: Option<EntryType>,
pub const_value: Option<EntryConst>,
}
impl GenericBenchEntry {
pub(crate) fn raw_name(&self) -> &str {
match (&self.ty, &self.const_value) {
(_, Some(const_value)) => const_value.name(),
(Some(ty), None) => ty.raw_name(),
(None, None) => unreachable!(),
}
}
pub(crate) fn display_name(&self) -> &str {
match (&self.ty, &self.const_value) {
(_, Some(const_value)) => const_value.name(),
(Some(ty), None) => ty.display_name(),
(None, None) => unreachable!(),
}
}
pub(crate) fn path_components(&self) -> impl Iterator<Item = &str> {
let module_path = self.group.meta.module_path_components();
let group_component = self.group.meta.raw_name;
let type_component = if self.const_value.is_some() {
self.ty.as_ref().map(|ty| ty.display_name())
} else {
None
};
module_path.chain(Some(group_component)).chain(type_component)
}
}
pub struct EntryType {
get_type_name: fn() -> &'static str,
#[allow(dead_code)]
get_type_id: fn() -> TypeId,
}
impl EntryType {
pub const fn new<T: Any>() -> Self {
Self { get_type_name: std::any::type_name::<T>, get_type_id: TypeId::of::<T> }
}
pub(crate) fn raw_name(&self) -> &'static str {
(self.get_type_name)()
}
pub(crate) fn display_name(&self) -> &'static str {
let mut type_name = self.raw_name();
while let Some((prev, next)) = type_name.split_once("::") {
if prev.contains('<') {
break;
}
type_name = next;
}
type_name
}
}
pub struct EntryConst {
value: *const (),
partial_cmp: unsafe fn(*const (), *const ()) -> Option<Ordering>,
to_string: unsafe fn(*const ()) -> String,
cached_string: ManuallyDrop<OnceLock<&'static str>>,
}
unsafe impl Send for EntryConst {}
unsafe impl Sync for EntryConst {}
impl EntryConst {
pub const fn new<T>(value: &'static T) -> Self
where
T: PartialOrd + ToString + Send + Sync,
{
unsafe fn partial_cmp<T: PartialOrd>(a: *const (), b: *const ()) -> Option<Ordering> {
T::partial_cmp(&*a.cast(), &*b.cast())
}
unsafe fn to_string<T: ToString>(value: *const ()) -> String {
T::to_string(&*value.cast())
}
Self {
value: value as *const T as *const (),
partial_cmp: partial_cmp::<T>,
to_string: to_string::<T>,
cached_string: ManuallyDrop::new(OnceLock::new()),
}
}
pub(crate) fn cmp_name(&self, other: &Self) -> Ordering {
if self.partial_cmp == other.partial_cmp {
if let Some(ordering) = unsafe { (self.partial_cmp)(self.value, other.value) } {
if !ordering.is_eq() {
return ordering;
}
}
}
self.name().cmp(other.name())
}
#[inline]
pub(crate) fn name(&self) -> &str {
self.cached_string.get_or_init(|| {
let string = unsafe { (self.to_string)(self.value) };
Box::leak(string.into_boxed_str())
})
}
}