From 30254cb5bb3ac1994b0c3aa33d55092b1d4e01db Mon Sep 17 00:00:00 2001 From: shimun Date: Sat, 1 Feb 2025 17:00:21 +0100 Subject: [PATCH 1/2] feat: added MappedBox --- src/lib.rs | 4 + src/mapped.rs | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 src/mapped.rs diff --git a/src/lib.rs b/src/lib.rs index d2b4509..ec4fbbc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,8 +38,10 @@ //!} //! ``` #![cfg_attr(not(test), no_std)] +#![feature(never_type)] mod atomic_bitset; +mod mapped; use core::cell::UnsafeCell; use core::future::{poll_fn, Future}; @@ -53,6 +55,8 @@ use portable_atomic::AtomicU32; use crate::atomic_bitset::AtomicBitset; +pub use crate::mapped::*; + /// Implementation detail. Not covered by semver guarantees. #[doc(hidden)] pub trait PoolStorage { diff --git a/src/mapped.rs b/src/mapped.rs new file mode 100644 index 0000000..2f8def8 --- /dev/null +++ b/src/mapped.rs @@ -0,0 +1,231 @@ +use core::{ + cell::UnsafeCell, + ops::{Deref, DerefMut}, + ptr::NonNull, +}; + +use crate::Box; +use crate::Pool; + +/// A structure that wraps a boxed item from a pool and provides a mapped reference to it. +/// +/// This is useful when a specific part of a pooled item needs to be accessed separately. +pub struct MappedBox { + inner: UnsafeCell>, // Inner storage of the pooled item. + value_ref: &'static mut T, // Mapped reference to a part of the pooled item. +} + +impl MappedBox { + /// Asynchronously creates a new `MappedBox`. + /// + /// # Arguments + /// * `item` - The item to be stored in the pool. + /// + /// # Returns + /// An `Option>>`, `None` if there is no waker available, Some(Err(_)) if `&mut P::Item` cannot be converted into `&mut T`. + pub async fn new_async(item: T) -> Option> + where + T: Into, + for<'a> &'a mut T: TryFrom<&'a mut P::Item, Error = E>, + { + if let Some(b) = Box::

::new_async(item.into()).await { + Some(b.convert()) + } else { + None + } + } + + /// Synchronously creates a new `MappedBox`. + /// + /// # Arguments + /// * `item` - The item to be stored in the pool. + /// + /// # Returns + /// An `Option>>`, `None` if the pool is full, Some(Err(_)) if `&mut P::Item` cannot be converted into `&mut T`. + pub async fn new(item: T) -> Option> + where + T: Into, + for<'a> &'a mut T: TryFrom<&'a mut P::Item, Error = E>, + { + if let Some(b) = Box::

::new(item.into()) { + Some(b.convert()) + } else { + None + } + } + + /// Returns an immutable reference to the mapped value. + pub fn as_ref<'a>(&'a self) -> &'a T { + &self.value_ref + } + + /// Returns a mutable reference to the mapped value. + pub fn as_mut<'a>(&'a mut self) -> &'a mut T { + &mut self.value_ref + } + + pub fn into_box(self) -> Box

{ + self.into() + } +} + +/// Extension trait providing mapping capabilities to `Box

`. +pub trait BoxExt: Sized { + /// Maps the current item into a `MappedBox` by applying a mapping function. + /// + /// # Arguments + /// * `mapper` - Function that maps the pooled item to another reference. + /// + /// # Returns + /// A `MappedBox` containing the mapped reference. + fn map<'a: 'static, M: 'a, F: FnOnce(&'a mut T) -> &'a mut M>( + self, + mapper: F, + ) -> MappedBox { + let Ok(m) = self.try_map(|item| Result::<_, !>::Ok(mapper(item))); + m + } + + /// Tries to map the current item into a `MappedBox`, returning an error if mapping fails. + /// + /// # Arguments + /// * `mapper` - Function that maps the pooled item to another reference, returning a `Result`. + /// + /// # Returns + /// A `Result, E>` indicating success or failure. + fn try_map<'a: 'static, M: 'a, E, F: FnOnce(&'a mut T) -> Result<&'a mut M, E>>( + self, + mapper: F, + ) -> Result, E>; + + /// Convert back into the original [`Box`] + fn into_box(self) -> Box

; + + /// Converts the current item into a `MappedBox` if the conversion is possible. + /// + /// # Returns + /// A `Result, E>` indicating whether the conversion succeeded. + fn convert<'a: 'static, M: 'a, E>(self) -> Result, E> + where + &'a mut M: TryFrom<&'a mut T, Error = E>, + { + self.try_map(|i| i.try_into()) + } +} + +impl BoxExt for Box

{ + fn try_map<'a: 'static, M: 'a, E, F: FnOnce(&'a mut P::Item) -> Result<&'a mut M, E>>( + self, + mapper: F, + ) -> Result, E> { + let inner = UnsafeCell::new(self); + Ok(MappedBox { + value_ref: unsafe { mapper(NonNull::new_unchecked(inner.get()).as_mut())? }, + inner, + }) + } + + fn into_box(self) -> Box

{ + self + } +} + +impl BoxExt for MappedBox { + fn try_map<'a: 'static, M: 'a, E, F: FnOnce(&'a mut T) -> Result<&'a mut M, E>>( + self, + mapper: F, + ) -> Result, E> { + Ok(MappedBox { + value_ref: mapper(self.value_ref)?, + inner: self.inner, + }) + } + + fn into_box(self) -> Box

{ + self.inner.into_inner() + } +} + +/// Allows conversion of `MappedBox` into its original boxed form. +impl Into> for MappedBox { + fn into(self) -> Box

{ + self.inner.into_inner() + } +} + +/// Implements `Deref` for `MappedBox`, allowing access to the mapped value. +impl Deref for MappedBox { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.value_ref + } +} + +/// Implements `DerefMut` for `MappedBox`, allowing mutable access to the mapped value. +impl DerefMut for MappedBox { + fn deref_mut(&mut self) -> &mut Self::Target { + self.value_ref + } +} + +#[cfg(test)] +mod tests { + use crate::pool; + + use super::*; + + #[derive(Debug)] + enum AB { + A(u32), + B(i32), + } + + impl<'a> TryFrom<&'a mut AB> for &'a mut u32 { + type Error = (); + + fn try_from(value: &'a mut AB) -> Result { + match value { + AB::A(u) => Ok(u), + _ => Err(()), + } + } + } + impl<'a> TryFrom<&'a mut AB> for &'a mut i32 { + type Error = (); + + fn try_from(value: &'a mut AB) -> Result { + match value { + AB::B(i) => Ok(i), + _ => Err(()), + } + } + } + + impl From for AB { + fn from(value: u32) -> Self { + AB::A(value) + } + } + impl From for AB { + fn from(value: i32) -> Self { + AB::B(value) + } + } + + pool!(TestPool: [AB; 4], 3); + + #[tokio::test] + async fn mapped() { + let a = MappedBox::::new_async(42u32) + .await + .expect("slot") + .expect("conversion"); + assert_eq!(*a, 42u32); + let b: MappedBox = a.into_box().map::(|item| { + *item = AB::B(-1); + item.try_into().unwrap() + }); + assert_eq!(*b, -1); + } +} From ab5aed2ac21e26bbaa212b950032ee2460ffc53e Mon Sep 17 00:00:00 2001 From: shimun Date: Mon, 3 Feb 2025 15:13:56 +0100 Subject: [PATCH 2/2] feat: relax Sized bound --- src/mapped.rs | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/src/mapped.rs b/src/mapped.rs index 2f8def8..1eda598 100644 --- a/src/mapped.rs +++ b/src/mapped.rs @@ -10,12 +10,12 @@ use crate::Pool; /// A structure that wraps a boxed item from a pool and provides a mapped reference to it. /// /// This is useful when a specific part of a pooled item needs to be accessed separately. -pub struct MappedBox { +pub struct MappedBox { inner: UnsafeCell>, // Inner storage of the pooled item. value_ref: &'static mut T, // Mapped reference to a part of the pooled item. } -impl MappedBox { +impl MappedBox { /// Asynchronously creates a new `MappedBox`. /// /// # Arguments @@ -70,7 +70,7 @@ impl MappedBox { } /// Extension trait providing mapping capabilities to `Box

`. -pub trait BoxExt: Sized { +pub trait BoxExt: Sized { /// Maps the current item into a `MappedBox` by applying a mapping function. /// /// # Arguments @@ -78,7 +78,7 @@ pub trait BoxExt: Sized { /// /// # Returns /// A `MappedBox` containing the mapped reference. - fn map<'a: 'static, M: 'a, F: FnOnce(&'a mut T) -> &'a mut M>( + fn map<'a: 'static, M: 'a + ?Sized, F: FnOnce(&'a mut T) -> &'a mut M>( self, mapper: F, ) -> MappedBox { @@ -93,7 +93,7 @@ pub trait BoxExt: Sized { /// /// # Returns /// A `Result, E>` indicating success or failure. - fn try_map<'a: 'static, M: 'a, E, F: FnOnce(&'a mut T) -> Result<&'a mut M, E>>( + fn try_map<'a: 'static, M: 'a + ?Sized, E, F: FnOnce(&'a mut T) -> Result<&'a mut M, E>>( self, mapper: F, ) -> Result, E>; @@ -114,7 +114,12 @@ pub trait BoxExt: Sized { } impl BoxExt for Box

{ - fn try_map<'a: 'static, M: 'a, E, F: FnOnce(&'a mut P::Item) -> Result<&'a mut M, E>>( + fn try_map< + 'a: 'static, + M: 'a + ?Sized, + E, + F: FnOnce(&'a mut P::Item) -> Result<&'a mut M, E>, + >( self, mapper: F, ) -> Result, E> { @@ -131,7 +136,7 @@ impl BoxExt for Box

{ } impl BoxExt for MappedBox { - fn try_map<'a: 'static, M: 'a, E, F: FnOnce(&'a mut T) -> Result<&'a mut M, E>>( + fn try_map<'a: 'static, M: 'a + ?Sized, E, F: FnOnce(&'a mut T) -> Result<&'a mut M, E>>( self, mapper: F, ) -> Result, E> { @@ -147,14 +152,14 @@ impl BoxExt for MappedBox { } /// Allows conversion of `MappedBox` into its original boxed form. -impl Into> for MappedBox { +impl Into> for MappedBox { fn into(self) -> Box

{ self.inner.into_inner() } } /// Implements `Deref` for `MappedBox`, allowing access to the mapped value. -impl Deref for MappedBox { +impl Deref for MappedBox { type Target = T; fn deref(&self) -> &Self::Target { @@ -163,12 +168,23 @@ impl Deref for MappedBox { } /// Implements `DerefMut` for `MappedBox`, allowing mutable access to the mapped value. -impl DerefMut for MappedBox { +impl DerefMut for MappedBox { fn deref_mut(&mut self) -> &mut Self::Target { self.value_ref } } +impl AsRef for MappedBox { + fn as_ref(&self) -> &T { + &self.value_ref + } +} +impl AsMut for MappedBox { + fn as_mut(&mut self) -> &mut T { + &mut self.value_ref + } +} + #[cfg(test)] mod tests { use crate::pool; @@ -228,4 +244,15 @@ mod tests { }); assert_eq!(*b, -1); } + + pool!(TestBufs: [[u8; 64]; 2], 3); + + #[tokio::test] + async fn map_to_slice() { + let mut array = Box::::new_async([0u8; 64]).await.expect("slot"); + // write some bytes + array[4..32].copy_from_slice(&[42u8; 28]); + let slice = array.map(|array| &mut array[4..32]); + assert_eq!(slice.as_ref(), &[42u8; 28]); + } }