diff options
| author | Mica White <botahamec@outlook.com> | 2025-12-08 20:13:20 -0500 |
|---|---|---|
| committer | Mica White <botahamec@outlook.com> | 2025-12-08 20:13:20 -0500 |
| commit | 6b1f814ea733699f28d9c60202ab8645cdd1c058 (patch) | |
| tree | 002b1c479ff079c7303a8b13302865b4344f4cae | |
| -rwxr-xr-x | .gitignore | 1 | ||||
| -rwxr-xr-x | Cargo.lock | 7 | ||||
| -rwxr-xr-x | Cargo.toml | 6 | ||||
| -rwxr-xr-x | rustfmt.toml | 3 | ||||
| -rwxr-xr-x | src/lib.rs | 244 |
5 files changed, 261 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100755 index 0000000..cda98f9 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "superpin" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100755 index 0000000..76af3ff --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "superpin" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100755 index 0000000..751a0aa --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,3 @@ +edition = "2021" +hard_tabs = true +newline_style = "Unix" diff --git a/src/lib.rs b/src/lib.rs new file mode 100755 index 0000000..08bc0b7 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,244 @@ +#![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)) + } + } +} |
