-
Notifications
You must be signed in to change notification settings - Fork 23
Expand file tree
/
Copy pathgeneric.rs
More file actions
180 lines (150 loc) · 5.5 KB
/
generic.rs
File metadata and controls
180 lines (150 loc) · 5.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
use std::{
any::{Any, TypeId},
cmp::Ordering,
mem::ManuallyDrop,
sync::OnceLock,
};
use crate::{
entry::{BenchEntryRunner, GroupEntry},
util::sort::natural_cmp,
};
/// Compile-time entry for a generic benchmark function, generated by
/// `#[divan::bench]`.
///
/// Unlike `BenchEntry`, this is for a specific generic type or `const`.
///
/// Although this type contains trivially-`Copy` data, it *should not* implement
/// `Clone` because the memory address of each instance is used to determine the
/// relative order in `GroupEntry.generic_benches` when sorting benchmarks by
/// location.
pub struct GenericBenchEntry {
/// The associated group, for entry metadata.
pub group: &'static GroupEntry,
/// The benchmarking function.
pub bench: BenchEntryRunner,
/// A generic type.
pub ty: Option<EntryType>,
/// A `const` value and associated data.
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();
// Generic benchmarks consider their group's raw name to be the path
// component after the module path.
let group_component = self.group.meta.raw_name;
// If this is a generic const benchmark with generic types, the generic
// types are considered to be the parent of the const values.
let type_component = if self.const_value.is_some() {
// FIXME: Switch back to `raw_name` once we have a way to insert
// this `display_name` into `EntryTree::Parent`. The current
// approach allows different types with the same name to become the
// same `EntryTree::Parent`.
self.ty.as_ref().map(|ty| ty.display_name())
} else {
None
};
module_path.chain(Some(group_component)).chain(type_component)
}
}
/// Generic type instantiation.
pub struct EntryType {
/// [`std::any::type_name`].
get_type_name: fn() -> &'static str,
/// [`std::any::TypeId::of`].
#[allow(dead_code)]
get_type_id: fn() -> TypeId,
}
impl EntryType {
/// Creates an instance for the given type.
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();
// Remove module components in type name.
while let Some((prev, next)) = type_name.split_once("::") {
// Do not go past generic type boundary.
if prev.contains('<') {
break;
}
type_name = next;
}
type_name
}
}
/// A reference to a `const` as a `&'static T`.
pub struct EntryConst {
/// `&'static T`.
value: *const (),
/// [`PartialOrd::partial_cmp`].
partial_cmp: unsafe fn(*const (), *const ()) -> Option<Ordering>,
/// [`ToString::to_string`].
to_string: unsafe fn(*const ()) -> String,
/// Cached `to_string` result.
cached_string: ManuallyDrop<OnceLock<&'static str>>,
}
// SAFETY: `T: Send + Sync`.
unsafe impl Send for EntryConst {}
unsafe impl Sync for EntryConst {}
impl EntryConst {
/// Creates entry data for a `const` values.
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()),
}
}
/// Returns [`PartialOrd::partial_cmp`] ordering if `<` or `>, falling back
/// to comparing [`ToString::to_string`] otherwise.
pub(crate) fn cmp_name(&self, other: &Self) -> Ordering {
if self.partial_cmp as usize == other.partial_cmp as usize {
// SAFETY: Both constants have the same comparison function, so they
// must be the same type.
if let Some(ordering) = unsafe { (self.partial_cmp)(self.value, other.value) } {
if !ordering.is_eq() {
return ordering;
}
}
}
// Fallback to name comparison.
natural_cmp(self.name(), other.name())
}
/// [`ToString::to_string`].
#[inline]
pub(crate) fn name(&self) -> &str {
self.cached_string.get_or_init(|| {
// SAFETY: The function is guaranteed to call `T::to_string`.
let string = unsafe { (self.to_string)(self.value) };
Box::leak(string.into_boxed_str())
})
}
}