From: Kostya Shishkov Date: Sat, 31 Jan 2026 12:37:18 +0000 (+0100) Subject: nihav_core: add self-borrowing box structure X-Git-Url: https://git.nihav.org/?a=commitdiff_plain;h=f56aa8ae0ac5a536b3c7547ee0ac1061c84890bc;p=nihav.git nihav_core: add self-borrowing box structure --- diff --git a/nihav-core/src/lib.rs b/nihav-core/src/lib.rs index bb803bc..82f4286 100644 --- a/nihav-core/src/lib.rs +++ b/nihav-core/src/lib.rs @@ -24,3 +24,5 @@ pub mod reorder; #[allow(clippy::upper_case_acronyms)] pub mod scale; pub mod soundcvt; + +pub mod sbbox; diff --git a/nihav-core/src/sbbox.rs b/nihav-core/src/sbbox.rs new file mode 100644 index 0000000..e1ee6e3 --- /dev/null +++ b/nihav-core/src/sbbox.rs @@ -0,0 +1,110 @@ +//! Self-borrowing structure. +//! +//! Occasionally it is necessary (or merely more convenient) to track both a certain object and some exclusive borrower of it e.g. file handle and a writer borrowing it for the duration of its lifetime. +//! While it is better to have a different design that e.g. incorporates borrowed object and returns it at the end or merely borrows it at each call, sometimes it is just easier to have a wrapper object that holds both borrowed and borrower object and grants exclusive access to one of them. +//! +//! `SelfBorrow` offers exactly such functionality. Since it has to rely on certain unsafe language features, users should use it with caution (and maybe consider `borrow` crate instead). + +use std::pin::Pin; +use std::alloc::{dealloc, Layout}; + +/// Self-borrowing structure that contains borrowed object of type `T` and its borrower of type `U`. +/// +/// It should be created with e.g. [`try_new`] and then use [`get_object_mut`] to access the borrower object. +/// When this object is no longer required, [`end`] may be called to get the original borrowed object back. +/// +/// # Example: +/// +/// ``` +/// use nihav_core::sbbox::*; +/// +/// struct PlusOne<'a> { v: &'a mut u8 } +/// impl<'a> PlusOne<'a> { +/// fn invoke(&mut self) { *self.v += 1; } +/// } +/// +/// let val = 42u8; +/// let mut sbbox = SelfBorrow::new(val, |v| PlusOne{ v: unsafe { v.as_mut().unwrap() } }); +/// sbbox.get_object_mut().invoke(); +/// let orig_val = SelfBorrow::end(sbbox); +/// println!("new value is {orig_val}"); +/// ``` +/// +/// [`try_new`]: ./struct.SelfBorrow.html#method.try_new +/// [`get_object_mut`]: ./struct.SelfBorrow.html#method.get_object_mut +/// [`end`]: ./struct.SelfBorrow.html#method.end +pub struct SelfBorrow { + bval: T, + dst: Option, +} + +impl SelfBorrow { + /// Creates a new self-borrow box from a borrowed object and a function taking a pointer to it. + pub fn new(src: T, create: F) -> Pin> + where F: Fn(*mut T) -> U { + let mut obj = Box::pin(Self{ + bval: src, + dst: None, + }); + let ptr = &mut obj.bval as *mut T; + obj.dst = Some(create(ptr)); + obj + } + /// Attempts to create a new self-borrow box from a borrowed object and a function taking a pointer to it. + /// + /// In case `create` function fails, the original object will be returned instead. + pub fn try_new(src: T, create: F) -> Result>, T> + where F: Fn(*mut T) -> Option { + let mut obj = Box::pin(Self{ + bval: src, + dst: None, + }); + let ptr = &mut obj.bval as *mut T; + let dst = create(ptr); + if dst.is_some() { + obj.dst = dst; + Ok(obj) + } else { + let ret = Self::end(obj); + Err(ret) + } + } + /// Returns an immutable reference to the borrower object. + pub fn get_object(&self) -> &U { + if let Some(ref dst) = self.dst { + dst + } else { + unreachable!() + } + } + /// Returns a mutable reference to the borrower object. + pub fn get_object_mut(&mut self) -> &mut U { + if let Some(ref mut dst) = self.dst { + dst + } else { + unreachable!() + } + } + /// Consumes the self-reference box and returns the original borrowed object. + pub fn end(mut boxed: SBBox) -> T { + boxed.dst = None; + unsafe { + let mut unboxed = Pin::into_inner(boxed); + let ret_ = std::mem::MaybeUninit::uninit(); + let mut ret = ret_.assume_init(); + std::mem::swap(&mut ret, &mut unboxed.bval); + dealloc(Box::into_raw(unboxed) as *mut u8, Layout::new::>()); + ret + } + } +} + +impl Drop for SelfBorrow { + fn drop(&mut self) { + // borrower should be dropped first + self.dst = None; + } +} + +/// Handy alias for self-borrowing box +pub type SBBox = Pin>>;