summaryrefslogtreecommitdiff
path: root/src/lib.rs
blob: 08bc0b76b018260d04862152bb6b2b978be7c348 (plain)
#![no_std]

extern crate alloc;

use alloc::boxed::Box;
use alloc::rc::Rc;
use alloc::sync::Arc;
use core::cmp;
use core::fmt::{self, Debug, Display, Pointer};
use core::hash::{Hash, Hasher};
use core::ops::{Deref, DerefMut};

#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct SuperPin<Ptr> {
	pointer: Ptr,
}

#[macro_export]
macro_rules! superpin {
	($value: expr $(,)?) => {
		$crate::SuperPin<&mut _> { pointer: &mut { $value } }
	};
}

impl<Ptr: Debug> Debug for SuperPin<Ptr> {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		fmt::Debug::fmt(&self.pointer, f)
	}
}

impl<Ptr: Display> Display for SuperPin<Ptr> {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		fmt::Display::fmt(&self.pointer, f)
	}
}

impl<Ptr: Pointer> Pointer for SuperPin<Ptr> {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		fmt::Pointer::fmt(&self.pointer, f)
	}
}

impl<Ptr: Deref, Q: Deref> PartialEq<SuperPin<Q>> for SuperPin<Ptr>
where
	Ptr::Target: PartialEq<Q::Target>,
{
	fn eq(&self, other: &SuperPin<Q>) -> bool {
		Ptr::Target::eq(self, other)
	}

	#[allow(clippy::partialeq_ne_impl)] // We need to copy what the Ptr type does
	fn ne(&self, other: &SuperPin<Q>) -> bool {
		Ptr::Target::ne(self, other)
	}
}

impl<Ptr: Deref<Target: Eq>> Eq for SuperPin<Ptr> {}

impl<Ptr: Deref, Q: Deref> PartialOrd<SuperPin<Q>> for SuperPin<Ptr>
where
	Ptr::Target: PartialOrd<Q::Target>,
{
	fn partial_cmp(&self, other: &SuperPin<Q>) -> Option<cmp::Ordering> {
		Ptr::Target::partial_cmp(self, other)
	}

	fn lt(&self, other: &SuperPin<Q>) -> bool {
		Ptr::Target::lt(self, other)
	}

	fn le(&self, other: &SuperPin<Q>) -> bool {
		Ptr::Target::le(self, other)
	}

	fn gt(&self, other: &SuperPin<Q>) -> bool {
		Ptr::Target::gt(self, other)
	}

	fn ge(&self, other: &SuperPin<Q>) -> bool {
		Ptr::Target::ge(self, other)
	}
}

impl<Ptr: Deref<Target: Ord>> Ord for SuperPin<Ptr> {
	fn cmp(&self, other: &Self) -> cmp::Ordering {
		Ptr::Target::cmp(self, other)
	}
}

impl<Ptr: Deref> Hash for SuperPin<Ptr>
where
	<Ptr as Deref>::Target: Hash,
{
	fn hash<H: Hasher>(&self, state: &mut H) {
		self.pointer.hash(state);
	}
}

impl<Ptr: Deref> Deref for SuperPin<Ptr> {
	type Target = Ptr::Target;

	fn deref(&self) -> &Self::Target {
		&self.pointer
	}
}

impl<Ptr: DerefMut> DerefMut for SuperPin<Ptr> {
	fn deref_mut(&mut self) -> &mut Self::Target {
		&mut self.pointer
	}
}

impl<T: ?Sized> From<Box<T>> for SuperPin<Box<T>> {
	fn from(value: Box<T>) -> Self {
		// safety: It's not possible to move or replace the insides of a
		//         SuperPin<Box<T>>, so it's safe to pin it directly without
		//         any additional requirements.
		unsafe { Self::new_unchecked(value) }
	}
}

impl<Ptr> SuperPin<Ptr> {
	pub fn boxed<T>(x: T) -> SuperPin<Box<T>> {
		SuperPin::from(Box::new(x))
	}

	pub fn rc<T>(value: T) -> SuperPin<Rc<T>> {
		// safety: It's not possible to move anything behind an Rc
		unsafe { SuperPin::new_unchecked(Rc::new(value)) }
	}

	pub fn arc<T>(value: T) -> SuperPin<Arc<T>> {
		// safety: It's not possible to move anything behind an Arc
		unsafe { SuperPin::new_unchecked(Arc::new(value)) }
	}
}

impl<Ptr: Deref> SuperPin<Ptr> {
	pub unsafe fn new_unchecked(pointer: Ptr) -> Self {
		Self { pointer }
	}

	pub fn as_ref(&self) -> SuperPin<&<Ptr as Deref>::Target> {
		// safety: the pointee cannot move while it is pinned
		unsafe { SuperPin::new_unchecked(self.pointer.deref()) }
	}

	pub unsafe fn into_inner_unchecked(pin: SuperPin<Ptr>) -> Ptr {
		// safety: the caller must ensure that the value is not moved
		pin.pointer
	}
}

impl<Ptr: DerefMut> SuperPin<Ptr> {
	pub fn as_mut(&mut self) -> SuperPin<&mut <Ptr as Deref>::Target> {
		// safety: the pointee cannot move while it is pinned
		unsafe { SuperPin::new_unchecked(self.pointer.deref_mut()) }
	}

	pub fn set(&mut self, value: <Ptr as Deref>::Target)
	where
		<Ptr as Deref>::Target: Sized,
	{
		// safety: the destructor is run and a new valid value of the same type is
		//         put in its place, so no pinning invariant is violated
		*self.pointer.deref_mut() = value;
	}
}

impl<'a, T: ?Sized> SuperPin<&'a T> {
	pub unsafe fn map_unchecked<U: ?Sized>(self, func: impl FnOnce(&T) -> &U) -> SuperPin<&'a U> {
		let new_pointer = func(self.pointer);

		// safety: the caller is responsible for upholding the invariants of
		//         SuperPin::new_unchecked
		unsafe { SuperPin::new_unchecked(new_pointer) }
	}

	pub fn get_ref(self) -> &'a T {
		// safety: it is impossible to move out of a shared pointer
		self.pointer
	}
}

impl<'a, T: ?Sized> SuperPin<&'a mut T> {
	pub fn into_ref(self) -> SuperPin<&'a T> {
		// safety: it is impossible to move out of a shared pointer
		unsafe { SuperPin::new_unchecked(&*self.pointer) }
	}

	pub unsafe fn get_unchecked_mut(self) -> &'a mut T {
		// safety: the caller must ensure the value is not moved out of the pointer
		self.pointer
	}

	pub unsafe fn map_unchecked_mut<U: ?Sized>(
		self,
		func: impl FnOnce(&mut T) -> &mut U,
	) -> SuperPin<&'a mut U> {
		let pointer = &mut *self.pointer;
		let new_pointer = func(pointer);

		// safety: the caller must uphold the invariants of SuperPin::new_unchecked
		unsafe { SuperPin::new_unchecked(new_pointer) }
	}
}

impl<T: ?Sized> SuperPin<&'static T> {
	pub fn static_ref(r: &'static T) -> Self {
		// safety: T is borrowed for the 'static lifetime, which never ends
		unsafe { SuperPin::new_unchecked(r) }
	}
}

impl<T: ?Sized> SuperPin<&'static mut T> {
	pub fn static_mut(r: &'static mut T) -> Self {
		// safety: T is borrowed for the 'static lifetime, which never ends
		unsafe { SuperPin::new_unchecked(r) }
	}
}

impl<'a, T> SuperPin<&'a Option<T>> {
	pub fn as_option_ref(self) -> Option<SuperPin<&'a T>> {
		unsafe {
			SuperPin::get_ref(self)
				.as_ref()
				// safety: x has to be pinned, because it comes from self
				.map(|x| SuperPin::new_unchecked(x))
		}
	}
}

impl<'a, T> SuperPin<&'a mut Option<T>> {
	pub fn as_option_mut(self) -> Option<SuperPin<&'a mut T>> {
		unsafe {
			// safety: the value is never moved
			SuperPin::get_unchecked_mut(self)
				.as_mut()
				// safety: x has to be pinned, because it comes from self
				.map(|x| SuperPin::new_unchecked(x))
		}
	}
}