1use crate::ffi::{int::PositiveInt, unknown::UnknownVariant};
28#[cfg(doc)]
29use crate::object::{types::ObjectType, TopologyObject};
30#[cfg(feature = "hwloc-2_1_0")]
31use hwlocality_sys::HWLOC_TYPE_DEPTH_MEMCACHE;
32use hwlocality_sys::{
33 hwloc_get_type_depth_e, HWLOC_TYPE_DEPTH_BRIDGE, HWLOC_TYPE_DEPTH_MISC,
34 HWLOC_TYPE_DEPTH_MULTIPLE, HWLOC_TYPE_DEPTH_NUMANODE, HWLOC_TYPE_DEPTH_OS_DEVICE,
35 HWLOC_TYPE_DEPTH_PCI_DEVICE, HWLOC_TYPE_DEPTH_UNKNOWN,
36};
37#[cfg(any(test, feature = "proptest"))]
38use proptest::prelude::*;
39#[allow(unused)]
40#[cfg(test)]
41use similar_asserts::assert_eq;
42use std::{fmt, num::TryFromIntError};
43use thiserror::Error;
44
45pub type NormalDepth = PositiveInt;
47
48#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
52#[doc(alias = "hwloc_get_type_depth_e")]
53pub enum Depth {
54 Normal(NormalDepth),
56
57 #[doc(alias = "HWLOC_TYPE_DEPTH_NUMANODE")]
59 NUMANode,
60
61 #[doc(alias = "HWLOC_TYPE_DEPTH_BRIDGE")]
63 Bridge,
64
65 #[doc(alias = "HWLOC_TYPE_DEPTH_PCI_DEVICE")]
67 PCIDevice,
68
69 #[doc(alias = "HWLOC_TYPE_DEPTH_OS_DEVICE")]
71 OSDevice,
72
73 #[doc(alias = "HWLOC_TYPE_DEPTH_MISC")]
75 Misc,
76
77 #[cfg(feature = "hwloc-2_1_0")]
79 #[doc(alias = "HWLOC_TYPE_DEPTH_MEMCACHE")]
80 MemCache,
81 Unknown(UnknownVariant<hwloc_get_type_depth_e>),
86}
87impl Depth {
89 pub fn expect_normal(self) -> NormalDepth {
92 NormalDepth::try_from(self).expect("Not a normal object depth")
93 }
94
95 pub const VIRTUAL_DEPTHS: &'static [Self] = &[
97 #[cfg(feature = "hwloc-2_1_0")]
98 Self::MemCache,
99 Self::NUMANode,
100 Self::Bridge,
101 Self::PCIDevice,
102 Self::OSDevice,
103 Self::Misc,
104 ];
105
106 pub const MEMORY_DEPTHS: &'static [Self] = &[
108 #[cfg(feature = "hwloc-2_1_0")]
109 Self::MemCache,
110 Self::NUMANode,
111 ];
112
113 pub const IO_DEPTHS: &'static [Self] = &[Self::Bridge, Self::PCIDevice, Self::OSDevice];
115
116 pub(crate) unsafe fn from_hwloc(
132 value: hwloc_get_type_depth_e,
133 ) -> Result<Self, TypeToDepthError> {
134 match value {
135 normal if normal >= 0 => {
136 let normal = NormalDepth::try_from_c_int(normal)
137 .expect("NormalDepth should support all positive depths");
138 Ok(normal.into())
139 }
140 HWLOC_TYPE_DEPTH_UNKNOWN => Err(TypeToDepthError::Nonexistent),
141 HWLOC_TYPE_DEPTH_MULTIPLE => Err(TypeToDepthError::Multiple),
142 HWLOC_TYPE_DEPTH_NUMANODE => Ok(Self::NUMANode),
143 HWLOC_TYPE_DEPTH_BRIDGE => Ok(Self::Bridge),
144 HWLOC_TYPE_DEPTH_PCI_DEVICE => Ok(Self::PCIDevice),
145 HWLOC_TYPE_DEPTH_OS_DEVICE => Ok(Self::OSDevice),
146 HWLOC_TYPE_DEPTH_MISC => Ok(Self::Misc),
147 #[cfg(feature = "hwloc-2_1_0")]
148 HWLOC_TYPE_DEPTH_MEMCACHE => Ok(Self::MemCache),
149 other => Ok(Self::Unknown(UnknownVariant(other))),
150 }
151 }
152
153 pub(crate) fn to_hwloc(self) -> hwloc_get_type_depth_e {
155 match self {
156 Self::Normal(value) => value.to_c_int(),
157 Self::NUMANode => HWLOC_TYPE_DEPTH_NUMANODE,
158 Self::Bridge => HWLOC_TYPE_DEPTH_BRIDGE,
159 Self::PCIDevice => HWLOC_TYPE_DEPTH_PCI_DEVICE,
160 Self::OSDevice => HWLOC_TYPE_DEPTH_OS_DEVICE,
161 Self::Misc => HWLOC_TYPE_DEPTH_MISC,
162 #[cfg(feature = "hwloc-2_1_0")]
163 Self::MemCache => HWLOC_TYPE_DEPTH_MEMCACHE,
164 Self::Unknown(unknown) => unknown.get(),
165 }
166 }
167}
168#[cfg(any(test, feature = "proptest"))]
170impl Arbitrary for Depth {
171 type Parameters = <NormalDepth as Arbitrary>::Parameters;
172 type Strategy = prop::strategy::TupleUnion<(
173 prop::strategy::WA<
174 prop::strategy::Map<<NormalDepth as Arbitrary>::Strategy, fn(NormalDepth) -> Self>,
175 >,
176 prop::strategy::WA<prop::sample::Select<Self>>,
177 )>;
178
179 fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
180 prop_oneof![
181 4 => NormalDepth::arbitrary_with(args).prop_map(Self::Normal),
182 1 => prop::sample::select(Self::VIRTUAL_DEPTHS)
183 ]
184 }
185}
186impl Default for Depth {
188 fn default() -> Self {
189 Self::from(NormalDepth::default())
190 }
191}
192impl fmt::Display for Depth {
194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195 #[allow(clippy::wildcard_enum_match_arm)]
196 match self {
197 Self::Normal(d) => <NormalDepth as fmt::Display>::fmt(d, f),
198 abnormal => {
199 let s = format!("<{abnormal:?}>");
200 f.pad(&s)
201 }
202 }
203 }
204}
205impl From<NormalDepth> for Depth {
207 fn from(value: NormalDepth) -> Self {
208 Self::Normal(value)
209 }
210}
211impl PartialEq<NormalDepth> for Depth {
213 fn eq(&self, other: &NormalDepth) -> bool {
214 *self == Self::Normal(*other)
215 }
216}
217impl PartialEq<Depth> for NormalDepth {
219 fn eq(&self, other: &Depth) -> bool {
220 other == self
221 }
222}
223impl PartialEq<usize> for Depth {
225 fn eq(&self, other: &usize) -> bool {
226 Self::try_from(*other) == Ok(*self)
227 }
228}
229impl PartialEq<Depth> for usize {
231 fn eq(&self, other: &Depth) -> bool {
232 other == self
233 }
234}
235impl TryFrom<usize> for Depth {
237 type Error = TryFromIntError;
238
239 fn try_from(value: usize) -> Result<Self, TryFromIntError> {
240 NormalDepth::try_from(value).map(Self::from)
241 }
242}
243impl TryFrom<Depth> for NormalDepth {
245 type Error = Depth;
246
247 fn try_from(value: Depth) -> Result<Self, Depth> {
248 if let Depth::Normal(depth) = value {
249 Ok(depth)
250 } else {
251 Err(value)
252 }
253 }
254}
255impl TryFrom<Depth> for usize {
257 type Error = Depth;
258
259 fn try_from(value: Depth) -> Result<Self, Depth> {
260 NormalDepth::try_from(value).map(Self::from)
261 }
262}
263
264#[derive(Copy, Clone, Debug, Eq, Error, Hash, PartialEq)]
266pub enum TypeToDepthError {
267 #[doc(alias = "HWLOC_TYPE_DEPTH_UNKNOWN")]
269 #[error("no object of requested type exists in the topology")]
270 Nonexistent,
271
272 #[doc(alias = "HWLOC_TYPE_DEPTH_MULTIPLE")]
276 #[error("objects of requested type exist at different depths in the topology")]
277 Multiple,
278}
279
280#[cfg(test)]
281mod tests {
282 use crate::tests::assert_panics;
283
284 use super::*;
285 #[allow(unused)]
286 use similar_asserts::assert_eq;
287 use static_assertions::{assert_impl_all, assert_not_impl_any, assert_type_eq_all};
288 use std::{
289 error::Error,
290 fmt::{Binary, Debug, Display, LowerExp, LowerHex, Octal, Pointer, UpperExp, UpperHex},
291 hash::Hash,
292 io::{self, Read},
293 ops::Deref,
294 panic::UnwindSafe,
295 };
296
297 assert_impl_all!(Depth:
300 Copy, Debug, Default, Display, From<NormalDepth>, Hash,
301 PartialEq<NormalDepth>, PartialEq<usize>, Sized, Sync, TryFrom<usize>,
302 TryInto<NormalDepth>, TryInto<usize>, Unpin, UnwindSafe
303 );
304 assert_not_impl_any!(Depth:
305 Binary, Deref, Drop, Error, IntoIterator, LowerExp, LowerHex,
306 Octal, PartialOrd, Pointer, Read, UpperExp, UpperHex, fmt::Write,
307 io::Write
308 );
309 assert_type_eq_all!(NormalDepth, PositiveInt);
310 assert_impl_all!(TypeToDepthError:
311 Copy, Error, Hash, Sized, Sync, Unpin, UnwindSafe
312 );
313 assert_not_impl_any!(TypeToDepthError:
314 Binary, Default, Deref, Drop, IntoIterator, LowerExp, LowerHex, Octal,
315 PartialOrd, Pointer, Read, UpperExp, UpperHex, fmt::Write, io::Write
316 );
317
318 #[test]
319 fn special_values() {
320 assert_eq!(Depth::default(), Depth::from(NormalDepth::default()));
321 assert_eq!(
322 unsafe { Depth::from_hwloc(HWLOC_TYPE_DEPTH_UNKNOWN) },
324 Err(TypeToDepthError::Nonexistent)
325 );
326 assert_eq!(
327 unsafe { Depth::from_hwloc(HWLOC_TYPE_DEPTH_MULTIPLE) },
329 Err(TypeToDepthError::Multiple)
330 );
331 const RAW_DEPTHS: &[hwloc_get_type_depth_e] = &[
332 #[cfg(feature = "hwloc-2_1_0")]
333 {
334 HWLOC_TYPE_DEPTH_MEMCACHE
335 },
336 HWLOC_TYPE_DEPTH_NUMANODE,
337 HWLOC_TYPE_DEPTH_BRIDGE,
338 HWLOC_TYPE_DEPTH_PCI_DEVICE,
339 HWLOC_TYPE_DEPTH_OS_DEVICE,
340 HWLOC_TYPE_DEPTH_MISC,
341 ];
342 assert_eq!(RAW_DEPTHS.len(), Depth::VIRTUAL_DEPTHS.len());
343 for (&raw, &depth) in RAW_DEPTHS.iter().zip(Depth::VIRTUAL_DEPTHS) {
344 assert_eq!(unsafe { Depth::from_hwloc(raw) }, Ok(depth))
346 }
347 }
348
349 proptest! {
350 #[test]
351 fn unary(depth: Depth) {
352 prop_assert!(matches!(depth, Depth::Normal(_)) || Depth::VIRTUAL_DEPTHS.contains(&depth));
354 if let Depth::Normal(normal) = depth {
355 prop_assert_eq!(depth.to_string(), normal.to_string());
356 prop_assert_eq!(NormalDepth::try_from(depth), Ok(normal));
357 prop_assert_eq!(usize::try_from(depth).unwrap(), normal);
358 prop_assert_eq!(depth.expect_normal(), normal);
359 prop_assert!(depth.to_hwloc() >= 0);
360 } else {
361 prop_assert_eq!(depth.to_string(), format!("<{depth:?}>"));
362 prop_assert!(NormalDepth::try_from(depth).is_err());
363 prop_assert!(usize::try_from(depth).is_err());
364 assert_panics(|| depth.expect_normal())?;
365 prop_assert!(depth.to_hwloc() <= HWLOC_TYPE_DEPTH_NUMANODE);
366 }
367 }
368
369 #[test]
370 fn from_normal(normal: NormalDepth) {
371 prop_assert_eq!(Depth::from(normal), normal);
372 prop_assert_eq!(normal, Depth::from(normal));
373 }
374 }
375
376 fn mostly_small_usize() -> impl Strategy<Value = usize> {
379 prop_oneof![
380 4 => usize::from(NormalDepth::MIN)..usize::from(NormalDepth::MAX),
381 1 => any::<usize>()
382 ]
383 }
384
385 proptest! {
386 #[test]
387 fn from_usize(value in mostly_small_usize()) {
388 if value < usize::from(NormalDepth::MAX) {
389 prop_assert_eq!(Depth::try_from(value).unwrap(), value);
390 prop_assert_eq!(value, Depth::try_from(value).unwrap());
391 } else {
392 prop_assert!(Depth::try_from(value).is_err());
393 }
394 }
395
396 #[test]
397 fn from_raw(value: hwloc_get_type_depth_e) {
398 let depth_res = unsafe { Depth::from_hwloc(value) };
402 if value >= 0 {
403 prop_assert!(depth_res.is_ok());
404 prop_assert_eq!(depth_res.unwrap(), usize::try_from(value).unwrap());
405 } else if value == HWLOC_TYPE_DEPTH_UNKNOWN {
406 prop_assert_eq!(depth_res, Err(TypeToDepthError::Nonexistent));
407 } else if value == HWLOC_TYPE_DEPTH_MULTIPLE {
408 prop_assert_eq!(depth_res, Err(TypeToDepthError::Multiple));
409 } else if value
410 > -2 - hwloc_get_type_depth_e::try_from(Depth::VIRTUAL_DEPTHS.len()).unwrap()
411 {
412 prop_assert!(depth_res.is_ok());
413 } else {
414 prop_assert_eq!(depth_res, Ok(Depth::Unknown(UnknownVariant(value))));
415 }
416 }
417
418 #[test]
419 fn eq_int(depth: Depth, normal: NormalDepth) {
420 prop_assert_eq!(depth == normal, depth == Depth::Normal(normal));
421 }
422
423 #[test]
424 fn eq_usize(depth: Depth, value: usize) {
425 prop_assert_eq!(
426 depth == value,
427 PositiveInt::try_from(value).is_ok_and(|value| depth == value)
428 );
429 }
430 }
431}