1use std::{
2 mem::ManuallyDrop,
3 num::NonZeroUsize,
4 sync::atomic::{AtomicUsize, Ordering::Relaxed},
5};
6
7use regex::Regex;
8
9#[macro_use]
10mod macros;
11
12pub mod fmt;
13pub mod sort;
14pub mod split_vec;
15pub mod sync;
16pub mod thread;
17pub mod ty;
18
19/// Public-in-private type like `()` but meant to be externally-unreachable.
20///
21/// Using this in place of `()` for `GenI` prevents `Bencher::with_inputs` from
22/// working with `()` unintentionally.
23#[non_exhaustive]
24pub struct Unit;
25
26/// Public-in-private trait to make `DivanConfig::skip_regex` polymorphic over
27/// regular expression types.
28pub trait IntoRegex {
29 fn into_regex(self) -> Regex;
30}
31
32impl IntoRegex for Regex {
33 #[inline]
34 fn into_regex(self) -> Regex {
35 self
36 }
37}
38
39impl IntoRegex for &str {
40 #[inline]
41 #[track_caller]
42 fn into_regex(self) -> Regex {
43 Regex::new(self).unwrap()
44 }
45}
46
47impl IntoRegex for String {
48 #[inline]
49 #[track_caller]
50 fn into_regex(self) -> Regex {
51 Regex::new(&self).unwrap()
52 }
53}
54
55/// [`std::hint::assert_unchecked`] polyfill.
56#[inline]
57pub(crate) const unsafe fn assert_unchecked(cond: bool) {
58 if !cond {
59 std::hint::unreachable_unchecked();
60 }
61}
62
63#[inline]
64pub(crate) fn defer<F: FnOnce()>(f: F) -> impl Drop {
65 struct Defer<F: FnOnce()>(ManuallyDrop<F>);
66
67 impl<F: FnOnce()> Drop for Defer<F> {
68 #[inline]
69 fn drop(&mut self) {
70 let f: F = unsafe { ManuallyDrop::take(&mut self.0) };
71
72 f();
73 }
74 }
75
76 Defer(ManuallyDrop::new(f))
77}
78
79/// Returns the index of `ptr` in the slice, assuming it is in the slice.
80#[inline]
81pub(crate) fn slice_ptr_index<T>(slice: &[T], ptr: *const T) -> usize {
82 // Safe pointer `offset_from`.
83 (ptr as usize - slice.as_ptr() as usize) / size_of::<T>()
84}
85
86/// Returns the values in the middle of `slice`.
87///
88/// If the slice has an even length, two middle values exist.
89#[inline]
90pub(crate) fn slice_middle<T>(slice: &[T]) -> &[T] {
91 let len: usize = slice.len();
92
93 if len == 0 {
94 slice
95 } else if len % 2 == 0 {
96 &slice[(len / 2) - 1..][..2]
97 } else {
98 &slice[len / 2..][..1]
99 }
100}
101
102/// Cached [`std::thread::available_parallelism`].
103#[inline]
104pub(crate) fn known_parallelism() -> NonZeroUsize {
105 static CACHED: AtomicUsize = AtomicUsize::new(0);
106
107 #[cold]
108 fn slow() -> NonZeroUsize {
109 let n: NonZero = std::thread::available_parallelism().unwrap_or(default:NonZeroUsize::MIN);
110
111 match CACHED.compare_exchange(current:0, new:n.get(), success:Relaxed, failure:Relaxed) {
112 Ok(_) => n,
113
114 // SAFETY: Zero is checked by us and competing threads.
115 Err(n: usize) => unsafe { NonZeroUsize::new_unchecked(n) },
116 }
117 }
118
119 match NonZeroUsize::new(CACHED.load(order:Relaxed)) {
120 Some(n: NonZero) => n,
121 None => slow(),
122 }
123}
124
125/// Returns `true` if running under [`cargo-nextest`](https://nexte.st).
126pub(crate) fn is_cargo_nextest() -> bool {
127 std::env::var_os(key:"NEXTEST").unwrap_or_default() == "1"
128}
129
130#[cfg(test)]
131mod tests {
132 use crate::black_box;
133
134 use super::*;
135
136 #[test]
137 fn known_parallelism() {
138 let f: fn() -> NonZeroUsize = super::known_parallelism;
139 assert_eq!(black_box(f)(), black_box(f)());
140 }
141
142 #[test]
143 fn slice_middle() {
144 use super::slice_middle;
145
146 assert_eq!(slice_middle::<i32>(&[]), &[]);
147
148 assert_eq!(slice_middle(&[1]), &[1]);
149 assert_eq!(slice_middle(&[1, 2]), &[1, 2]);
150 assert_eq!(slice_middle(&[1, 2, 3]), &[2]);
151 assert_eq!(slice_middle(&[1, 2, 3, 4]), &[2, 3]);
152 assert_eq!(slice_middle(&[1, 2, 3, 4, 5]), &[3]);
153 }
154}
155