commonlibsse_ng\re\b/
BSTSmartPointer.rs

1//! # BSTSmartPointer
2//!
3//! A smart pointer with custom reference counting and auto-ptr management strategies.
4
5use crate::re::BSIntrusiveRefCounted::BSIntrusiveRefCountedTrait;
6use crate::re::TESBox::TESBox;
7use core::fmt::{self, Debug};
8use core::marker::PhantomData;
9use core::ops::{Deref, DerefMut};
10use core::ptr::{self, NonNull};
11use stdx::ptr::Unique;
12
13/// Trait for managing smart pointer lifetimes.
14pub trait ManageBSTSmartPointer {
15    /// No-op for acquire.
16    #[inline]
17    fn acquire<T>(_ptr: *mut T)
18    where
19        T: BSIntrusiveRefCountedTrait,
20    {
21    }
22
23    /// Releases the object held by the smart pointer.
24    ///
25    /// # Safety
26    ///
27    /// The pointer must be valid and allocated by `TESBox`.
28    #[inline]
29    unsafe fn release<T>(ptr: *mut T)
30    where
31        T: BSIntrusiveRefCountedTrait,
32    {
33        if !ptr.is_null() {
34            drop(unsafe { TESBox::from_raw(ptr) });
35        }
36    }
37}
38
39/// Intrusive reference counting manager.
40#[derive(Debug)]
41pub struct BSTSmartPointerIntrusiveRefCount;
42
43impl ManageBSTSmartPointer for BSTSmartPointerIntrusiveRefCount {
44    #[inline]
45    #[allow(clippy::not_unsafe_ptr_arg_deref)]
46    fn acquire<T>(ptr: *mut T)
47    where
48        T: BSIntrusiveRefCountedTrait,
49    {
50        if !ptr.is_null() {
51            (unsafe { &*ptr }).inc_ref();
52        }
53    }
54
55    #[inline]
56    unsafe fn release<T>(ptr: *mut T)
57    where
58        T: BSIntrusiveRefCountedTrait,
59    {
60        if !ptr.is_null() && (unsafe { &*ptr }).dec_ref() == 0 {
61            drop(unsafe { TESBox::from_raw(ptr) });
62        }
63    }
64}
65
66/// Auto-pointer manager without reference counting.
67#[derive(Debug)]
68pub struct BSTSmartPointerAutoPtr;
69impl ManageBSTSmartPointer for BSTSmartPointerAutoPtr {}
70
71pub type BSTAutoPointer<T> = BSTSmartPointer<T, BSTSmartPointerAutoPtr>;
72
73/// Smart pointer with customizable reference management.
74///
75/// This smart pointer optionally manages reference counts depending on the `M` parameter.
76///
77/// # Panics
78///
79/// Dereferencing a null pointer via `Deref` or `DerefMut` will **panic**.
80/// Use [`as_ref`] or [`is_null`] to avoid this panic.
81#[repr(C)]
82pub struct BSTSmartPointer<T, M = BSTSmartPointerIntrusiveRefCount>
83where
84    T: BSIntrusiveRefCountedTrait,
85    M: ManageBSTSmartPointer,
86{
87    /// C++ equivalent: `T*`
88    ptr: Option<Unique<T>>,
89    _marker: PhantomData<M>,
90}
91
92impl<T, M> BSTSmartPointer<T, M>
93where
94    T: BSIntrusiveRefCountedTrait,
95    M: ManageBSTSmartPointer,
96{
97    /// Creates a new smart pointer from a raw pointer.
98    /// # Safety
99    /// The pointer must be created from `TESBox`(`MemoryManager::allocate`).
100    #[inline]
101    pub unsafe fn new(ptr: *mut T) -> Self {
102        M::acquire(ptr);
103        Self { ptr: Unique::new(ptr), _marker: PhantomData }
104    }
105
106    /// Creates a new smart pointer from a raw pointer.
107    #[inline]
108    pub fn from_non_null(ptr: NonNull<T>) -> Self {
109        M::acquire(ptr.as_ptr());
110        Self { ptr: Some(Unique::from(ptr)), _marker: PhantomData }
111    }
112
113    /// Replaces the internal pointer with null and releases the old object.
114    ///
115    /// After calling this, [`is_null`] returns `true`.
116    #[inline]
117    pub fn reset(&mut self) {
118        unsafe {
119            M::release(self.as_ptr());
120            self.ptr = None; // <- ptr::null_mut()
121        }
122    }
123
124    /// Creates a smart pointer from a [`TESBox`], taking ownership.
125    #[inline]
126    pub fn from_box(value: TESBox<T>) -> Self {
127        let ptr = TESBox::into_raw(value);
128        // Safety: The pointer is valid and managed by `TESBox`.
129        unsafe { Self::new(ptr) }
130    }
131
132    /// Returns an immutable reference to the managed object, or `None` if the pointer is null.
133    ///
134    /// Use this to safely access the internal object without risking a panic.
135    #[inline]
136    pub const fn as_ref(&self) -> Option<&T> {
137        match &self.ptr {
138            Some(p) => unsafe { Some(p.as_ref()) },
139            None => None,
140        }
141    }
142
143    /// Returns a mutable reference to the managed object, or `None` if the pointer is null.
144    #[inline]
145    pub const fn as_mut(&mut self) -> Option<&mut T> {
146        match &mut self.ptr {
147            Some(p) => unsafe { Some(p.as_mut()) },
148            None => None,
149        }
150    }
151
152    /// Returns the raw pointer to the managed object.
153    #[inline]
154    pub const fn as_ptr(&self) -> *mut T {
155        match self.ptr {
156            Some(p) => p.as_ptr(),
157            None => ptr::null_mut(),
158        }
159    }
160
161    /// Returns true if the internal pointer is null.
162    #[inline]
163    pub const fn is_null(&self) -> bool {
164        self.ptr.is_none()
165    }
166}
167
168impl<T, M> fmt::Debug for BSTSmartPointer<T, M>
169where
170    T: BSIntrusiveRefCountedTrait + fmt::Debug,
171    M: ManageBSTSmartPointer,
172{
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        let mut s = f.debug_struct("BSTSmartPointer");
175        match self.as_ref() {
176            Some(value) => s.field("ptr", value),
177            None => s.field("ptr", &"null"),
178        };
179        s.finish()
180    }
181}
182
183impl<T, M> Default for BSTSmartPointer<T, M>
184where
185    T: BSIntrusiveRefCountedTrait,
186    M: ManageBSTSmartPointer,
187{
188    #[inline]
189    fn default() -> Self {
190        Self { ptr: None, _marker: PhantomData }
191    }
192}
193
194impl<T, M> Drop for BSTSmartPointer<T, M>
195where
196    T: BSIntrusiveRefCountedTrait,
197    M: ManageBSTSmartPointer,
198{
199    #[inline]
200    fn drop(&mut self) {
201        self.reset();
202    }
203}
204
205impl<T, M> Deref for BSTSmartPointer<T, M>
206where
207    T: BSIntrusiveRefCountedTrait,
208    M: ManageBSTSmartPointer,
209{
210    type Target = T;
211
212    /// Dereferences the smart pointer to the managed object.
213    ///
214    /// # Panics
215    ///
216    /// Panics if the internal pointer is null.
217    #[inline]
218    fn deref(&self) -> &Self::Target {
219        self.as_ref().expect("Dereferencing null pointer")
220    }
221}
222
223impl<T, M> DerefMut for BSTSmartPointer<T, M>
224where
225    T: BSIntrusiveRefCountedTrait,
226    M: ManageBSTSmartPointer,
227{
228    /// Dereferences the smart pointer mutably.
229    ///
230    /// # Panics
231    ///
232    /// Panics if the internal pointer is null.
233    #[inline]
234    fn deref_mut(&mut self) -> &mut Self::Target {
235        self.as_mut().expect("Dereferencing null pointer")
236    }
237}
238
239impl<T, M> Clone for BSTSmartPointer<T, M>
240where
241    T: BSIntrusiveRefCountedTrait,
242    M: ManageBSTSmartPointer,
243{
244    #[inline]
245    fn clone(&self) -> Self {
246        M::acquire(self.as_ptr());
247        Self { ptr: self.ptr, _marker: PhantomData }
248    }
249}
250
251impl<T, M> PartialEq for BSTSmartPointer<T, M>
252where
253    T: BSIntrusiveRefCountedTrait,
254    M: ManageBSTSmartPointer,
255{
256    #[inline]
257    fn eq(&self, other: &Self) -> bool {
258        match (self.ptr, other.ptr) {
259            (Some(p1), Some(p2)) => p1.as_non_null_ptr() == p2.as_non_null_ptr(),
260            (None, None) => true,
261            _ => false,
262        }
263    }
264}
265
266impl<T, M> Eq for BSTSmartPointer<T, M>
267where
268    T: BSIntrusiveRefCountedTrait,
269    M: ManageBSTSmartPointer,
270{
271}
272
273impl<T, M> PartialEq<*mut T> for BSTSmartPointer<T, M>
274where
275    T: BSIntrusiveRefCountedTrait,
276    M: ManageBSTSmartPointer,
277{
278    #[inline]
279    fn eq(&self, other: &*mut T) -> bool {
280        self.as_ptr() == *other
281    }
282}
283
284impl<T, M> PartialEq<Option<*mut T>> for BSTSmartPointer<T, M>
285where
286    T: BSIntrusiveRefCountedTrait,
287    M: ManageBSTSmartPointer,
288{
289    #[inline]
290    fn eq(&self, other: &Option<*mut T>) -> bool {
291        Some(self.as_ptr()) == *other
292    }
293}
294
295impl<T, M> PartialEq<ptr::NonNull<T>> for BSTSmartPointer<T, M>
296where
297    T: BSIntrusiveRefCountedTrait,
298    M: ManageBSTSmartPointer,
299{
300    #[inline]
301    fn eq(&self, other: &ptr::NonNull<T>) -> bool {
302        self.as_ptr() == other.as_ptr()
303    }
304}
305
306#[cfg(test)]
307mod tests {
308    use super::*;
309    use core::sync::atomic::{AtomicU32, Ordering};
310
311    #[repr(C)]
312    #[derive(Debug)]
313    struct TestObject {
314        ref_count: AtomicU32,
315        value: i32,
316    }
317
318    impl TestObject {
319        const fn new(value: i32) -> Self {
320            Self { ref_count: AtomicU32::new(0), value }
321        }
322    }
323
324    impl BSIntrusiveRefCountedTrait for TestObject {
325        fn inc_ref(&self) -> u32 {
326            self.ref_count.fetch_add(1, Ordering::AcqRel) + 1
327        }
328
329        fn dec_ref(&self) -> u32 {
330            self.ref_count.fetch_sub(1, Ordering::AcqRel) - 1
331        }
332    }
333    // BSTSmartPointerTrait,
334
335    #[test]
336    fn test_smart_pointer() {
337        {
338            let obj = TESBox::new(TestObject::new(42));
339            let mut ptr = BSTSmartPointer::<TestObject>::from_box(obj);
340            assert_eq!(ptr.value, 42);
341            assert!(ptr.as_ref().is_some());
342            assert_eq!(ptr.as_ref().map(|p| p.ref_count.load(Ordering::Acquire)), Some(1));
343
344            // Clone and check ref count
345            let mut ptr2 = ptr.clone();
346            assert_eq!(ptr2.value, 42);
347            assert_eq!(ptr.as_ref().map(|p| p.ref_count.load(Ordering::Acquire)), Some(2));
348
349            ptr.reset();
350            assert_eq!(ptr2.as_ref().map(|p| p.ref_count.load(Ordering::Acquire)), Some(1));
351
352            assert!(ptr.as_ref().is_none());
353            assert!(ptr2.as_ref().is_some());
354            ptr2.reset();
355        }
356    }
357
358    #[test]
359    fn test_auto_pointer() {
360        let obj = TESBox::new(TestObject::new(123));
361        let mut auto_ptr = BSTAutoPointer::from_box(obj);
362        assert_eq!(auto_ptr.value, 123);
363
364        auto_ptr.reset();
365        assert!(auto_ptr.as_ref().is_none());
366    }
367}