wyz/
comu.rs

1/*! Trait-level `co`nst/`mu`table tracking.
2
3This module provides a system of marker types that can be used to encode write
4permissions into type parameters rather than duplicate structures.
5!*/
6
7//  This module has no compute logic of its own; it only exists in the
8//  type-system and to forward to the standard library.
9#![cfg(not(tarpaulin_include))]
10
11use core::{
12	cmp,
13	convert::TryFrom,
14	fmt::{
15		self,
16		Debug,
17		Display,
18		Formatter,
19		Pointer,
20	},
21	hash::{
22		Hash,
23		Hasher,
24	},
25	ops::Deref,
26	ptr::NonNull,
27	slice,
28};
29
30use tap::Pipe;
31
32/// A basic `const` marker.
33#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
34pub struct Const;
35
36/// A basic `mut` marker.
37#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
38pub struct Mut;
39
40/// A frozen wrapper over some other `Mutability` marker.
41#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
42pub struct Frozen<Inner>
43where Inner: Mutability
44{
45	inner: Inner,
46}
47
48/** Generalized mutability permissions.
49
50This trait enables referent structures to be generic over the write permissions
51of their referent data. As an example, the standard library defines `*const T`
52and `*mut T` as two duplicate type families, that cannot share any logic at all.
53
54An equivalent library implementation might be `Ptr<T, M: Mutability>`, where
55shared logic can be placed in an `impl<T, M> Ptr<T, M>` block, but unique logic
56(such as freezing a `Mut` pointer, or unfreezing a `Frozen<Mut>`) can be placed
57in specialized `impl<T> Ptr<T, Mut>` blocks.
58**/
59pub trait Mutability: 'static + Copy + Sized + self::seal::Sealed {
60	/// Marks whether this type contains mutability permissions within it.
61	///
62	/// This is `false` for `Const` and `true` for `Mut`. `Frozen` wrappers
63	/// atop either of these types inherit their interior marker.
64	const CONTAINS_MUTABILITY: bool = false;
65
66	/// Counts the layers of `Frozen<>` wrapping around a base `Const` or `Mut`.
67	const PEANO_NUMBER: usize = 0;
68
69	/// Allow instances to be constructed generically.
70	const SELF: Self;
71
72	/// One of `*const` or `*mut`.
73	const RENDER: &'static str;
74
75	/// Freeze this type, wrapping it in a `const` marker that may later be
76	/// removed to thaw it.
77	fn freeze(self) -> Frozen<Self> {
78		Frozen { inner: self }
79	}
80
81	/// Thaw a previously-frozen type, removing its `Frozen` marker and
82	/// restoring it to `Self`.
83	fn thaw(Frozen { inner }: Frozen<Self>) -> Self {
84		inner
85	}
86}
87
88impl Mutability for Const {
89	const RENDER: &'static str = "*const";
90	const SELF: Self = Self;
91}
92
93impl self::seal::Sealed for Const {
94}
95
96impl<Inner> Mutability for Frozen<Inner>
97where Inner: Mutability + Sized
98{
99	const CONTAINS_MUTABILITY: bool = Inner::CONTAINS_MUTABILITY;
100	const PEANO_NUMBER: usize = 1 + Inner::PEANO_NUMBER;
101	const RENDER: &'static str = Inner::RENDER;
102	const SELF: Self = Self { inner: Inner::SELF };
103}
104
105impl<Inner> self::seal::Sealed for Frozen<Inner> where Inner: Mutability + Sized
106{
107}
108
109impl Mutability for Mut {
110	const CONTAINS_MUTABILITY: bool = true;
111	const RENDER: &'static str = "*mut";
112	const SELF: Self = Self;
113}
114
115impl self::seal::Sealed for Mut {
116}
117
118/** A generic non-null pointer with type-system mutability tracking.
119
120# Type Parameters
121
122- `M`: The mutability permissions of the source pointer.
123- `T`: The referent type of the source pointer.
124**/
125pub struct Address<M, T>
126where
127	M: Mutability,
128	T: ?Sized,
129{
130	/// The address value.
131	inner: NonNull<T>,
132	/// The mutability permissions.
133	comu: M,
134}
135
136impl<M, T> Address<M, T>
137where M: Mutability
138{
139	/// The dangling pointer.
140	pub const DANGLING: Self = Self {
141		inner: NonNull::dangling(),
142		comu: M::SELF,
143	};
144}
145
146impl<M, T> Address<M, T>
147where
148	M: Mutability,
149	T: ?Sized,
150{
151	/// Constructs a new `Address` over some pointer value.
152	///
153	/// You are responsible for selecting the correct `Mutability` marker.
154	#[inline(always)]
155	pub fn new(addr: NonNull<T>) -> Self {
156		Self {
157			inner: addr,
158			comu: M::SELF,
159		}
160	}
161
162	/// Permanently converts an `Address<_>` into an `Address<Const>`.
163	///
164	/// You should generally prefer [`Address::freeze`].
165	#[inline(always)]
166	pub fn immut(self) -> Address<Const, T> {
167		Address {
168			inner: self.inner,
169			comu: Const,
170		}
171	}
172
173	/// Force an `Address<Const>` to be `Address<Mut>`.
174	///
175	/// ## Safety
176	///
177	/// You should only call this on addresses you know to have been created
178	/// with `Mut`able permissions and previously removed by [`Address::immut`].
179	///
180	/// You should prefer using [`Address::freeze`] for temporary, trackable,
181	/// immutability constraints instead.
182	#[inline(always)]
183	pub unsafe fn assert_mut(self) -> Address<Mut, T> {
184		Address {
185			inner: self.inner,
186			comu: Mut,
187		}
188	}
189
190	/// Freezes the `Address` so that it is read-only.
191	#[inline(always)]
192	pub fn freeze(self) -> Address<Frozen<M>, T> {
193		let Self { inner, comu } = self;
194		Address {
195			inner,
196			comu: comu.freeze(),
197		}
198	}
199
200	/// Removes the `Address` type marker, returning the original pointer.
201	#[inline(always)]
202	pub fn into_inner(self) -> NonNull<T> {
203		self.inner
204	}
205
206	/// Gets the address as a read-only pointer.
207	#[inline(always)]
208	pub fn to_const(self) -> *const T {
209		self.inner.as_ptr() as *const T
210	}
211}
212
213impl<T> Address<Mut, T> {
214	/// Gets the address as a write-capable pointer.
215	#[inline(always)]
216	#[allow(clippy::wrong_self_convention)]
217	pub fn to_mut(self) -> *mut T {
218		self.inner.as_ptr()
219	}
220}
221
222impl<M, T> Address<Frozen<M>, T>
223where
224	M: Mutability,
225	T: ?Sized,
226{
227	/// Thaws the `Address` to its original mutability permission.
228	#[inline(always)]
229	pub fn thaw(self) -> Address<M, T> {
230		let Self { inner, comu } = self;
231		Address {
232			inner,
233			comu: Mutability::thaw(comu),
234		}
235	}
236}
237
238/// Implement `*T -> *T` functions as `Address<T> -> Address<T>`.
239macro_rules! fwd {
240	($(
241		$(@$unsafe:ident)?
242		$name:ident
243		$(<
244			$($lt:lifetime),*
245			$($typaram:ident$(: $($bound:ident),+ $(,)?)?),*
246			$(,)*
247		>)?
248		$(, $arg:ident: $ty:ty)*
249		$(=> $ret:ty)?
250	);+ $(;)?) => { $(
251		#[doc = concat!("Applies `<*T>::", stringify!($name), "`.")]
252		///
253		/// See [original documentation][orig].
254		///
255		#[doc = concat!("[orig]: https://doc.rust-lang.org/std/primitive.pointer.html#method.", stringify!($name))]
256		pub $($unsafe)? fn $name$(<
257			$($lt,)* $($typaram$(: $($bound),+)?,)*
258		>)?(self$(, $arg: $ty)*) $(-> $ret)? {
259			self.with_ptr(|ptr| ptr.$name($($arg),*))
260		}
261	)+ };
262}
263
264/// Implement all other pointer functions.
265macro_rules! map {
266	($(
267		$(@$unsafe:ident)?
268		$name:ident
269		$(<
270			$($lt:lifetime),*
271			$($typaram:ident$(: $($bound:ident),+ $(,)?)?),*
272			$(,)?
273		>)?
274		$(, $arg:ident: $ty:ty $(as $map:expr)?)*
275		$(=> $ret:ty)?
276	);+ $(;)?) => { $(
277		#[doc = concat!("Applies `<*T>::", stringify!($name), "`.")]
278		///
279		/// See [original documentation][orig].
280		///
281		#[doc = concat!("[orig]: https://doc.rust-lang.org/std/primitive.pointer.html#method.", stringify!($name))]
282		pub $($unsafe)? fn $name$(<
283			$($lt,)* $($typaram$(: $($bound),+)?,)*
284		>)?(self$(, $arg: $ty)*) $(-> $ret)? {
285			self.inner.as_ptr().$name($($arg$(.pipe($map))?),*)
286		}
287	)+ };
288}
289
290/// Port of the pointer inherent methods on `Address`es of `Sized` types.
291#[allow(clippy::missing_safety_doc)]
292impl<M, T> Address<M, T>
293where M: Mutability
294{
295	fwd! {
296		cast<U> => Address<M, U>;
297		@unsafe offset, count: isize => Self;
298		@unsafe add, count: usize => Self;
299		@unsafe sub, count: usize => Self;
300		wrapping_offset, count: isize => Self;
301		wrapping_add, count: usize => Self;
302		wrapping_sub, count: usize => Self;
303	}
304
305	map! {
306		@unsafe offset_from, origin: Self as |orig| orig.to_const() as *mut T => isize;
307		@unsafe read => T;
308		@unsafe read_volatile => T;
309		@unsafe read_unaligned => T;
310		@unsafe copy_to, dest: Address<Mut, T> as Address::to_mut, count: usize;
311		@unsafe copy_to_nonoverlapping, dest: Address<Mut, T> as Address::to_mut, count: usize;
312		align_offset, align: usize => usize;
313	}
314}
315
316/// Port of the pointer inherent methods on `Address`es of any type.
317impl<M, T> Address<M, T>
318where
319	M: Mutability,
320	T: ?Sized,
321{
322	map! {
323		@unsafe as_ref<'a> => Option<&'a T>;
324	}
325
326	/// Applies a pointer -> pointer function within an Address -> Address.
327	#[track_caller]
328	fn with_ptr<U>(self, func: impl FnOnce(*mut T) -> *mut U) -> Address<M, U> {
329		self.inner
330			.as_ptr()
331			.pipe(func)
332			.pipe(NonNull::new)
333			.unwrap()
334			.pipe(Address::new)
335	}
336}
337
338/// Port of pointer inherent methods on mutable `Address`es of sized types.
339impl<T> Address<Mut, T> {
340	map! {
341		@unsafe copy_from<M2: Mutability>, src: Address<M2, T> as Address::to_const, count: usize;
342		@unsafe copy_from_nonoverlapping<M2: Mutability>, src: Address<M2, T> as Address::to_const, count: usize;
343		@unsafe write, value: T;
344		@unsafe write_volatile, value: T;
345		@unsafe write_unaligned, value: T;
346		@unsafe replace, src: T => T;
347		@unsafe swap, with: Self as Self::to_mut;
348	}
349}
350
351/// Port of pointer inherent methods on mutable `Address`es of any type.
352impl<T> Address<Mut, T>
353where T: ?Sized
354{
355	map! {
356		@unsafe as_mut<'a> => Option<&'a mut T>;
357		@unsafe drop_in_place;
358	}
359}
360
361impl<M, T> Clone for Address<M, T>
362where
363	M: Mutability,
364	T: ?Sized,
365{
366	#[inline(always)]
367	fn clone(&self) -> Self {
368		*self
369	}
370}
371
372impl<T> TryFrom<*const T> for Address<Const, T>
373where T: ?Sized
374{
375	type Error = NullPtrError;
376
377	#[inline(always)]
378	fn try_from(elem: *const T) -> Result<Self, Self::Error> {
379		NonNull::new(elem as *mut T)
380			.ok_or(NullPtrError)
381			.map(Self::new)
382	}
383}
384
385impl<T> From<&T> for Address<Const, T>
386where T: ?Sized
387{
388	#[inline(always)]
389	fn from(elem: &T) -> Self {
390		Self::new(elem.into())
391	}
392}
393
394impl<T> TryFrom<*mut T> for Address<Mut, T>
395where T: ?Sized
396{
397	type Error = NullPtrError;
398
399	#[inline(always)]
400	fn try_from(elem: *mut T) -> Result<Self, Self::Error> {
401		NonNull::new(elem).ok_or(NullPtrError).map(Self::new)
402	}
403}
404
405impl<T> From<&mut T> for Address<Mut, T>
406where T: ?Sized
407{
408	#[inline(always)]
409	fn from(elem: &mut T) -> Self {
410		Self::new(elem.into())
411	}
412}
413
414impl<M, T> Eq for Address<M, T> where M: Mutability
415{
416}
417
418impl<M1, M2, T1, T2> PartialEq<Address<M2, T2>> for Address<M1, T1>
419where
420	M1: Mutability,
421	M2: Mutability,
422{
423	#[inline]
424	fn eq(&self, other: &Address<M2, T2>) -> bool {
425		self.inner.as_ptr() as usize == other.inner.as_ptr() as usize
426	}
427}
428
429impl<M, T> Ord for Address<M, T>
430where M: Mutability
431{
432	#[inline]
433	fn cmp(&self, other: &Self) -> cmp::Ordering {
434		self.partial_cmp(other)
435			.expect("Addresses have a total ordering")
436	}
437}
438
439impl<M1, M2, T1, T2> PartialOrd<Address<M2, T2>> for Address<M1, T1>
440where
441	M1: Mutability,
442	M2: Mutability,
443{
444	#[inline]
445	fn partial_cmp(&self, other: &Address<M2, T2>) -> Option<cmp::Ordering> {
446		(self.inner.as_ptr() as usize)
447			.partial_cmp(&(other.inner.as_ptr() as usize))
448	}
449}
450
451impl<M, T> Debug for Address<M, T>
452where
453	M: Mutability,
454	T: ?Sized,
455{
456	#[inline(always)]
457	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
458		Debug::fmt(&self.to_const(), fmt)
459	}
460}
461
462impl<M, T> Pointer for Address<M, T>
463where
464	M: Mutability,
465	T: ?Sized,
466{
467	#[inline(always)]
468	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
469		Pointer::fmt(&self.to_const(), fmt)
470	}
471}
472
473impl<M, T> Hash for Address<M, T>
474where
475	M: Mutability,
476	T: ?Sized,
477{
478	#[inline(always)]
479	fn hash<H>(&self, state: &mut H)
480	where H: Hasher {
481		self.inner.hash(state)
482	}
483}
484
485impl<M, T> Copy for Address<M, T>
486where
487	M: Mutability,
488	T: ?Sized,
489{
490}
491
492impl<M, T> self::seal::Sealed for Address<M, T>
493where
494	M: Mutability,
495	T: ?Sized,
496{
497}
498
499/// Allows an `Address` to produce an ordinary reference.
500pub trait Referential<'a>: self::seal::Sealed {
501	/// The created reference type. Must be one of `&T` or `&mut T`.
502	type Ref: 'a + Deref;
503
504	/// Converts the `Address` to a reference.
505	///
506	/// ## Safety
507	///
508	/// The caller is responsible for ensuring that the memory location that the
509	/// `Address` describes contains an initialized value, and that the produced
510	/// reference abides by the Rust `&`/`&mut` exclusion rules.
511	unsafe fn to_ref(self) -> Self::Ref;
512
513	/// Converts a reference back into an `Address`.
514	fn from_ref(this: Self::Ref) -> Self;
515}
516
517impl<'a, T> Referential<'a> for Address<Const, T>
518where T: 'a + ?Sized
519{
520	type Ref = &'a T;
521
522	unsafe fn to_ref(self) -> Self::Ref {
523		self.inner.as_ref()
524	}
525
526	fn from_ref(this: Self::Ref) -> Self {
527		this.into()
528	}
529}
530
531impl<'a, T> Referential<'a> for Address<Mut, T>
532where T: 'a + ?Sized
533{
534	type Ref = &'a mut T;
535
536	unsafe fn to_ref(mut self) -> Self::Ref {
537		self.inner.as_mut()
538	}
539
540	fn from_ref(this: Self::Ref) -> Self {
541		this.into()
542	}
543}
544
545impl<'a, M, T> Referential<'a> for Address<Frozen<M>, T>
546where
547	M: Mutability,
548	T: 'a + ?Sized,
549{
550	type Ref = &'a T;
551
552	unsafe fn to_ref(self) -> Self::Ref {
553		self.inner.as_ref()
554	}
555
556	fn from_ref(this: Self::Ref) -> Self {
557		Self::new(NonNull::from(this))
558	}
559}
560
561/// A generically-mutable reference.
562pub type Reference<'a, M, T> = <Address<M, T> as Referential<'a>>::Ref;
563
564/// Allows an `Address<M, [T]>` to produce an ordinary slice reference.
565pub trait SliceReferential<'a>: Referential<'a> + self::seal::Sealed {
566	/// The type of the element pointer.
567	type ElementAddr;
568
569	/// Constructs an ordinary slice reference from a base-address and a length.
570	///
571	/// ## Parameters
572	///
573	/// - `ptr`: The address of the base element in the slice.
574	/// - `len`: The number of elements, beginning at `ptr`, in the slice.
575	///
576	/// ## Safety
577	///
578	/// The base address and the element count must describe a valid region of
579	/// memory.
580	unsafe fn from_raw_parts(ptr: Self::ElementAddr, len: usize) -> Self::Ref;
581}
582
583impl<'a, T> SliceReferential<'a> for Address<Const, [T]>
584where T: 'a
585{
586	type ElementAddr = Address<Const, T>;
587
588	unsafe fn from_raw_parts(ptr: Self::ElementAddr, len: usize) -> Self::Ref {
589		slice::from_raw_parts(ptr.to_const(), len)
590	}
591}
592
593impl<'a, M, T> SliceReferential<'a> for Address<Frozen<M>, [T]>
594where
595	M: Mutability,
596	T: 'a,
597{
598	type ElementAddr = Address<Frozen<M>, T>;
599
600	unsafe fn from_raw_parts(ptr: Self::ElementAddr, len: usize) -> Self::Ref {
601		slice::from_raw_parts(ptr.to_const(), len)
602	}
603}
604
605impl<'a, T> SliceReferential<'a> for Address<Mut, [T]>
606where T: 'a
607{
608	type ElementAddr = Address<Mut, T>;
609
610	unsafe fn from_raw_parts(ptr: Self::ElementAddr, len: usize) -> Self::Ref {
611		slice::from_raw_parts_mut(ptr.to_mut(), len)
612	}
613}
614
615/// [`Address`] cannot be constructed over null pointers.
616#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
617pub struct NullPtrError;
618
619impl Display for NullPtrError {
620	#[inline]
621	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
622		write!(fmt, "wyz::Address cannot contain a null pointer")
623	}
624}
625
626#[cfg(feature = "std")]
627impl std::error::Error for NullPtrError {
628}
629
630#[doc(hidden)]
631mod seal {
632	#[doc(hidden)]
633	pub trait Sealed {}
634}