]> git.nihav.org Git - nihav.git/commitdiff
nihav_core: add self-borrowing box structure
authorKostya Shishkov <kostya.shishkov@gmail.com>
Sat, 31 Jan 2026 12:37:18 +0000 (13:37 +0100)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Sat, 31 Jan 2026 12:37:18 +0000 (13:37 +0100)
nihav-core/src/lib.rs
nihav-core/src/sbbox.rs [new file with mode: 0644]

index bb803bc910fc767c2b35b2e09b4af5798eb90241..82f42863bc1d3cae00bd79549ce55f2162dcf10e 100644 (file)
@@ -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 (file)
index 0000000..e1ee6e3
--- /dev/null
@@ -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<T, U> {
+    bval: T,
+    dst:  Option<U>,
+}
+
+impl<T: Unpin, U: Unpin> SelfBorrow<T, U> {
+    /// Creates a new self-borrow box from a borrowed object and a function taking a pointer to it.
+    pub fn new<F>(src: T, create: F) -> Pin<Box<Self>>
+            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<F>(src: T, create: F) -> Result<Pin<Box<Self>>, T>
+            where F: Fn(*mut T) -> Option<U> {
+        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, U>) -> 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::<SelfBorrow<T, U>>());
+            ret
+        }
+    }
+}
+
+impl<T, U> Drop for SelfBorrow<T, U> {
+    fn drop(&mut self) {
+        // borrower should be dropped first
+        self.dst = None;
+    }
+}
+
+/// Handy alias for self-borrowing box
+pub type SBBox<T, U> = Pin<Box<SelfBorrow<T, U>>>;