Skip to content

Commit

Permalink
Add internal type casting utilities
Browse files Browse the repository at this point in the history
The `TypeCast` trait allows using `cast_ref` as a method.

We allow casting from a non-`Any`, which makes it possible to optimize
some internals without adding `Any` to more of the public API.
  • Loading branch information
nvzqz committed Jun 30, 2024
1 parent 5f15370 commit 8db2763
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 21 deletions.
10 changes: 5 additions & 5 deletions src/bench/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{
sync::OnceLock,
};

use crate::{util, Bencher};
use crate::{util::ty::TypeCast, Bencher};

/// Holds lazily-initialized runtime arguments to be passed into a benchmark.
///
Expand Down Expand Up @@ -73,21 +73,21 @@ impl BenchArgs {
// Collect printable representations of arguments.
let names: &'static [&str] = 'names: {
// PERF: Reuse items allocation as-is.
if let Some(args) = util::cast_ref::<&[&str]>(&args) {
if let Some(args) = args.cast_ref::<&[&str]>() {
break 'names args;
}

Box::leak(
args.iter()
.map(|arg| -> &str {
// PERF: Use strings as-is.
if let Some(arg) = util::cast_ref::<String>(arg) {
if let Some(arg) = arg.cast_ref::<String>() {
return arg;
}
if let Some(arg) = util::cast_ref::<Box<str>>(arg) {
if let Some(arg) = arg.cast_ref::<Box<str>>() {
return arg;
}
if let Some(arg) = util::cast_ref::<Cow<str>>(arg) {
if let Some(arg) = arg.cast_ref::<Cow<str>>() {
return arg;
}

Expand Down
10 changes: 5 additions & 5 deletions src/counter/any_counter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
BytesCount, BytesFormat, CharsCount, CyclesCount, IntoCounter, ItemsCount, MaxCountUInt,
},
time::FineDuration,
util::{self, fmt::DisplayThroughput},
util::{fmt::DisplayThroughput, ty::TypeCast},
};

/// Type-erased `Counter`.
Expand All @@ -23,13 +23,13 @@ impl AnyCounter {
pub(crate) fn new<C: IntoCounter>(counter: C) -> Self {
let counter = counter.into_counter();

if let Some(bytes) = util::cast_ref::<BytesCount>(&counter) {
if let Some(bytes) = counter.cast_ref::<BytesCount>() {
Self::bytes(bytes.count)
} else if let Some(chars) = util::cast_ref::<CharsCount>(&counter) {
} else if let Some(chars) = counter.cast_ref::<CharsCount>() {
Self::chars(chars.count)
} else if let Some(cycles) = util::cast_ref::<CyclesCount>(&counter) {
} else if let Some(cycles) = counter.cast_ref::<CyclesCount>() {
Self::cycles(cycles.count)
} else if let Some(items) = util::cast_ref::<ItemsCount>(&counter) {
} else if let Some(items) = counter.cast_ref::<ItemsCount>() {
Self::items(items.count)
} else {
unreachable!()
Expand Down
12 changes: 1 addition & 11 deletions src/util/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::{
any::{Any, TypeId},
num::NonZeroUsize,
sync::atomic::{AtomicUsize, Ordering::Relaxed},
};

pub mod fmt;
pub mod sync;
pub mod thread;
pub mod ty;

/// Public-in-private type like `()` but meant to be externally-unreachable.
///
Expand All @@ -15,16 +15,6 @@ pub mod thread;
#[non_exhaustive]
pub struct Unit;

#[inline]
pub(crate) fn cast_ref<T: Any>(r: &impl Any) -> Option<&T> {
if r.type_id() == TypeId::of::<T>() {
// SAFETY: `r` is `&T`.
Some(unsafe { &*(r as *const _ as *const T) })
} else {
None
}
}

/// Returns the index of `ptr` in the slice, assuming it is in the slice.
#[inline]
pub(crate) fn slice_ptr_index<T>(slice: &[T], ptr: *const T) -> usize {
Expand Down
38 changes: 38 additions & 0 deletions src/util/ty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use std::{
any::{Any, TypeId},
marker::PhantomData,
};

/// Returns a [`TypeId`] for any type regardless of whether it is `'static`.
///
/// Note that **this is not the same** as [`TypeId::of`].
#[inline]
pub(crate) fn proxy_type_id<T: ?Sized>() -> TypeId {
// Return the type ID of a generic closure.
Any::type_id(&|| PhantomData::<T>)
}

/// Returns `true` if the given types are equal.
#[inline]
pub(crate) fn is_type_eq<A: ?Sized, B: ?Sized>() -> bool {
proxy_type_id::<A>() == proxy_type_id::<B>()
}

/// Convenience trait for type conversions.
pub(crate) trait TypeCast {
/// Converts a reference if `self` is an instance of `T`.
///
/// We require `T: 'static` since we want to ensure when providing a type
/// that any lifetimes are static, such as `Cow<str>`.
#[inline]
fn cast_ref<T: 'static>(&self) -> Option<&T> {
if is_type_eq::<Self, T>() {
// SAFETY: `self` is `&T`.
Some(unsafe { &*(self as *const Self as *const T) })
} else {
None
}
}
}

impl<A> TypeCast for A {}

0 comments on commit 8db2763

Please sign in to comment.