bitvec/ptr/
addr.rs

1#![doc = include_str!("../../doc/ptr/addr.md")]
2
3use core::{
4	any,
5	fmt::{
6		self,
7		Debug,
8		Display,
9		Formatter,
10		Pointer,
11	},
12	mem,
13	ptr::NonNull,
14};
15
16use tap::{
17	Pipe,
18	TryConv,
19};
20use wyz::{
21	comu::{
22		Address,
23		Const,
24		Mut,
25		Mutability,
26	},
27	fmt::FmtForward,
28};
29
30/// Ensures that an address is well-aligned to its referent type width.
31#[inline]
32pub fn check_alignment<M, T>(
33	addr: Address<M, T>,
34) -> Result<Address<M, T>, MisalignError<T>>
35where M: Mutability {
36	let ptr = addr.to_const();
37	let mask = mem::align_of::<T>() - 1;
38	if ptr as usize & mask != 0 {
39		Err(MisalignError { ptr })
40	}
41	else {
42		Ok(addr)
43	}
44}
45
46/// Extension methods for raw pointers.
47pub(crate) trait AddressExt {
48	/// Tracks the original mutation capability of the source pointer.
49	type Permission: Mutability;
50	/// The type to which the pointer points.
51	type Referent: Sized;
52
53	/// Forcibly wraps a raw pointer as an `Address`, without handling errors.
54	///
55	/// In debug builds, this panics on null or misaligned pointers. In release
56	/// builds, it is permitted to remove the error-handling codepaths and
57	/// assume these invariants are upheld by the caller.
58	///
59	/// ## Safety
60	///
61	/// The caller must ensure that this is only called on non-null,
62	/// well-aligned pointers. Pointers derived from Rust references or calls to
63	/// the Rust allocator API will always satisfy this.
64	unsafe fn into_address(self) -> Address<Self::Permission, Self::Referent>;
65}
66
67#[cfg(not(tarpaulin_include))]
68impl<T> AddressExt for *const T {
69	type Permission = Const;
70	type Referent = T;
71
72	unsafe fn into_address(self) -> Address<Const, T> {
73		if cfg!(debug_assertions) {
74			self.try_conv::<Address<_, _>>()
75				.unwrap_or_else(|err| panic!("{}", err))
76				.pipe(check_alignment)
77				.unwrap_or_else(|err| panic!("{}", err))
78		}
79		else {
80			Address::new(NonNull::new_unchecked(self as *mut T))
81		}
82	}
83}
84
85#[cfg(not(tarpaulin_include))]
86impl<T> AddressExt for *mut T {
87	type Permission = Mut;
88	type Referent = T;
89
90	unsafe fn into_address(self) -> Address<Mut, T> {
91		if cfg!(debug_assertions) {
92			self.try_conv::<Address<_, _>>()
93				.unwrap_or_else(|err| panic!("{}", err))
94				.pipe(check_alignment)
95				.unwrap_or_else(|err| panic!("{}", err))
96		}
97		else {
98			Address::new(NonNull::new_unchecked(self))
99		}
100	}
101}
102
103#[cfg(not(tarpaulin_include))]
104impl<T> AddressExt for &T {
105	type Permission = Const;
106	type Referent = T;
107
108	unsafe fn into_address(self) -> Address<Self::Permission, Self::Referent> {
109		self.into()
110	}
111}
112
113#[cfg(not(tarpaulin_include))]
114impl<T> AddressExt for &mut T {
115	type Permission = Mut;
116	type Referent = T;
117
118	unsafe fn into_address(self) -> Address<Self::Permission, Self::Referent> {
119		self.into()
120	}
121}
122
123/// The error produced when an address is insufficiently aligned to the width of
124/// its type.
125#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
126pub struct MisalignError<T> {
127	/// The misaligned pointer.
128	ptr: *const T,
129}
130
131impl<T> MisalignError<T> {
132	/// The minimum address alignment of `T` values.
133	const ALIGN: usize = mem::align_of::<T>();
134	/// The number of least-significant-bits of an address that must be `0` in
135	/// order for it to be validly aligned for `T`.
136	const CTTZ: usize = Self::ALIGN.trailing_zeros() as usize;
137}
138
139#[cfg(not(tarpaulin_include))]
140impl<T> Debug for MisalignError<T> {
141	#[inline]
142	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
143		fmt.debug_tuple("MisalignError")
144			.field(&self.ptr.fmt_pointer())
145			.field(&Self::ALIGN)
146			.finish()
147	}
148}
149
150#[cfg(not(tarpaulin_include))]
151impl<T> Display for MisalignError<T> {
152	#[inline]
153	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
154		write!(
155			fmt,
156			"Type {} requires {}-byte alignment: address ",
157			any::type_name::<T>(),
158			Self::ALIGN,
159		)?;
160		Pointer::fmt(&self.ptr, fmt)?;
161		write!(fmt, " must clear its least {} bits", Self::CTTZ)
162	}
163}
164
165unsafe impl<T> Send for MisalignError<T> {}
166
167unsafe impl<T> Sync for MisalignError<T> {}
168
169#[cfg(feature = "std")]
170impl<T> std::error::Error for MisalignError<T> {}