summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ripple/minitrace/src/syscall_abi/device.rs95
-rw-r--r--ripple/minitrace/src/syscall_abi/mod.rs8
2 files changed, 101 insertions, 2 deletions
diff --git a/ripple/minitrace/src/syscall_abi/device.rs b/ripple/minitrace/src/syscall_abi/device.rs
new file mode 100644
index 0000000..743a02a
--- /dev/null
+++ b/ripple/minitrace/src/syscall_abi/device.rs
@@ -0,0 +1,95 @@
+// SPDX-FileCopyrightText: edef <edef@unfathomable.blue>
+// SPDX-License-Identifier: OSL-3.0
+
+use std::{
+	fmt::{self, Debug},
+	num::NonZeroU64,
+};
+
+/// `Device` represents a non-zero `libc::dev_t`.
+/// `Option<Device>` 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<Device> {
+		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::<Device>);
+			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()));
+		}
+	}
+}
diff --git a/ripple/minitrace/src/syscall_abi/mod.rs b/ripple/minitrace/src/syscall_abi/mod.rs
index e050711..31fd2e7 100644
--- a/ripple/minitrace/src/syscall_abi/mod.rs
+++ b/ripple/minitrace/src/syscall_abi/mod.rs
@@ -10,10 +10,14 @@ use {
 	},
 };
 
-pub(crate) use arg::{ProcessSyscallArg, SyscallArg};
-pub(crate) use fd::{DirFd, FileDesc};
+pub(crate) use {
+	arg::{ProcessSyscallArg, SyscallArg},
+	device::Device,
+	fd::{DirFd, FileDesc},
+};
 
 mod arg;
+mod device;
 mod fd;
 pub mod macros;