// SPDX-FileCopyrightText: edef // SPDX-License-Identifier: OSL-3.0 use std::{ fmt::{self, Debug}, num::NonZeroU64, }; /// `Device` represents a non-zero `libc::dev_t`. /// `Option` has identical representation to `libc::dev_t`. #[repr(transparent)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub(crate) struct Device(NonZeroU64); impl Debug for Device { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Device({}, {})", self.major(), self.minor()) } } impl Device { pub(crate) fn new(major: u32, minor: u32) -> Device { Self::checked_new(major, minor).expect("zero major *and* minor device number") } pub(crate) fn checked_new(major: u32, minor: u32) -> Option { let major = major as u64; let minor = minor as u64; let dev = 0 | (major & 0x00000fff) << 8 | (major & 0xfffff000) << 32 | (minor & 0x000000ff) << 0 | (minor & 0xffffff00) << 12; NonZeroU64::new(dev).map(Device) } pub(crate) fn dev(&self) -> u64 { self.0.into() } pub(crate) fn major(&self) -> u32 { let mut major = 0; major |= (self.dev() & 0x00000000000fff00) >> 8; major |= (self.dev() & 0xfffff00000000000) >> 32; major as u32 } pub(crate) fn minor(&self) -> u32 { let mut minor = 0; minor |= (self.dev() & 0x00000000000000ff) >> 0; minor |= (self.dev() & 0x00000ffffff00000) >> 12; minor as u32 } } #[cfg(test)] mod test { use {super::Device, nix::libc, std::mem}; #[test] #[should_panic = "zero major *and* minor device number"] fn no_zero() { Device::new(0, 0); } #[test] fn none_repr() { unsafe { let dev: libc::dev_t = mem::transmute(None::); assert_eq!(dev, 0); } } const GOLDEN: &[((u32, u32), libc::dev_t)] = &[ ((0xaaaaa555, 0xaaaaaa55), 0xaaaaaaaaaaa55555), ((0x55555aaa, 0x555555aa), 0x55555555555aaaaa), ((0xaaaaaaaa, 0x55555555), 0xaaaaa555555aaa55), ((0x12345678, 0xabcdef42), 0x12345abcdef67842), ]; #[test] fn golden() { for &golden @ ((major, minor), dev) in GOLDEN { let libc_dev = unsafe { libc::makedev(major, minor) }; if dev != libc_dev { panic!("broken test: reference data ({dev:#016x}) does not match libc ({libc_dev:#016x})"); } let d = Device::new(major, minor); assert_eq!(golden, ((d.major(), d.minor()), d.dev())); } } }