1pub(crate) trait SeenInodes {
2 fn already_seen(&self, key: (u64, u64)) -> bool;
3}
4
5#[cfg(feature = "rayon")]
6mod rayon_impl {
7 use std::num::NonZeroU16;
8
9 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11 pub enum Threads {
12 #[default]
13 All,
14 Exact(NonZeroU16),
15 }
16
17 impl From<u16> for Threads {
18 fn from(n: u16) -> Self {
19 match NonZeroU16::new(n) {
20 None => Self::All,
21 Some(n) => Self::Exact(n),
22 }
23 }
24 }
25
26 impl std::fmt::Display for Threads {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 match self {
29 Self::All => f.write_str("0"),
30 Self::Exact(n) => write!(f, "{n}"),
31 }
32 }
33 }
34
35 impl std::str::FromStr for Threads {
36 type Err = std::num::ParseIntError;
37
38 fn from_str(s: &str) -> Result<Self, Self::Err> {
39 let n: u16 = s.parse()?;
40 Ok(Self::from(n))
41 }
42 }
43
44 impl Threads {
45 pub fn num_threads(self) -> usize {
46 match self {
47 Self::All => 0,
48 Self::Exact(n) => n.get() as usize,
49 }
50 }
51 }
52
53 impl super::SeenInodes for dashmap::DashMap<(u64, u64), ()> {
54 fn already_seen(&self, key: (u64, u64)) -> bool {
55 self.insert(key, ()).is_some()
56 }
57 }
58
59 pub(crate) type InodeSet = dashmap::DashMap<(u64, u64), ()>;
60}
61
62#[cfg(feature = "rayon")]
63pub(crate) use rayon_impl::InodeSet;
64#[cfg(feature = "rayon")]
65pub use rayon_impl::Threads;
66
67#[cfg(not(feature = "rayon"))]
68impl SeenInodes for std::cell::RefCell<std::collections::HashSet<(u64, u64)>> {
69 fn already_seen(&self, key: (u64, u64)) -> bool {
70 !self.borrow_mut().insert(key)
71 }
72}
73
74#[cfg(not(feature = "rayon"))]
75pub(crate) type InodeSet = std::cell::RefCell<std::collections::HashSet<(u64, u64)>>;
76
77#[cfg(test)]
78#[cfg(feature = "rayon")]
79mod tests {
80 use std::num::NonZeroU16;
81
82 use super::*;
83
84 fn exact(n: u16) -> Threads {
85 Threads::Exact(NonZeroU16::new(n).unwrap())
86 }
87
88 #[test]
89 fn threads_from_zero_is_all() {
90 assert_eq!(Threads::from(0), Threads::All);
91 }
92
93 #[test]
94 fn threads_from_nonzero_is_exact() {
95 assert_eq!(Threads::from(4), exact(4));
96 assert_eq!(Threads::from(1), exact(1));
97 }
98
99 #[test]
100 fn threads_default_is_all() {
101 assert_eq!(Threads::default(), Threads::All);
102 }
103
104 #[test]
105 fn threads_num_threads_all_is_zero() {
106 assert_eq!(Threads::All.num_threads(), 0);
107 }
108
109 #[test]
110 fn threads_num_threads_exact() {
111 assert_eq!(exact(8).num_threads(), 8);
112 assert_eq!(exact(1).num_threads(), 1);
113 }
114
115 #[test]
116 fn threads_display() {
117 assert_eq!(Threads::All.to_string(), "0");
118 assert_eq!(exact(4).to_string(), "4");
119 }
120
121 #[test]
122 fn threads_from_str() {
123 assert_eq!("0".parse::<Threads>(), Ok(Threads::All));
124 assert_eq!("4".parse::<Threads>(), Ok(exact(4)));
125 assert_eq!("1".parse::<Threads>(), Ok(exact(1)));
126 assert!("abc".parse::<Threads>().is_err());
127 }
128
129 #[test]
130 fn threads_display_roundtrip() {
131 for t in [Threads::All, exact(1), exact(8)] {
132 assert_eq!(t.to_string().parse::<Threads>(), Ok(t));
133 }
134 }
135}