blind_pool/
local_pooled.rs

1use std::fmt;
2use std::ops::Deref;
3use std::pin::Pin;
4use std::rc::Rc;
5
6use crate::{LocalBlindPool, RawPooled};
7
8/// A reference to a value stored in a [`LocalBlindPool`].
9///
10/// This type provides automatic lifetime management for values in the pool.
11/// When the last [`LocalPooled`] instance for a value is dropped, the value
12/// is automatically removed from the pool.
13///
14/// Multiple [`LocalPooled`] instances can reference the same value through
15/// cloning, implementing reference counting semantics.
16///
17/// # Single-threaded Design
18///
19/// This type is designed for single-threaded use and is neither [`Send`] nor [`Sync`].
20///
21/// # Example
22///
23/// ```rust
24/// use blind_pool::LocalBlindPool;
25///
26/// let pool = LocalBlindPool::new();
27/// let value_handle = pool.insert("Test".to_string());
28///
29/// // Access the value through dereferencing.
30/// assert_eq!(*value_handle, "Test".to_string());
31///
32/// // Clone to create additional references.
33/// let cloned_handle = value_handle.clone();
34/// assert_eq!(*cloned_handle, "Test".to_string());
35/// ```
36pub struct LocalPooled<T: ?Sized> {
37    /// The reference-counted inner data containing the actual pooled item and pool handle.
38    inner: Rc<LocalPooledInner<T>>,
39}
40
41/// Internal data structure that manages the lifetime of a locally pooled item.
42///
43/// This is always type-erased to `()` and shared among all typed views of the same item.
44/// It ensures that the item is removed from the pool exactly once when all references are dropped.
45struct LocalPooledRef {
46    /// The type-erased handle to the actual item in the pool.
47    pooled: RawPooled<()>,
48
49    /// A handle to the pool that keeps it alive as long as this item exists.
50    pool: LocalBlindPool,
51}
52
53/// Internal data structure that contains the typed access to the locally pooled item.
54#[non_exhaustive]
55struct LocalPooledInner<T: ?Sized> {
56    /// The typed handle to the actual item in the pool.
57    pooled: RawPooled<T>,
58
59    /// A shared reference to the lifetime manager.
60    lifetime: Rc<LocalPooledRef>,
61}
62
63impl<T: ?Sized> LocalPooledInner<T> {
64    /// Creates a new [`LocalPooledInner<T>`] from a pooled item and pool handle.
65    ///
66    /// This is an internal constructor used when reconstructing pooled values
67    /// after type casting operations via the [`define_pooled_dyn_cast!`] macro.
68    #[must_use]
69    fn new(pooled: RawPooled<T>, pool: LocalBlindPool) -> Self {
70        let lifetime = Rc::new(LocalPooledRef {
71            pooled: pooled.erase(),
72            pool,
73        });
74        Self { pooled, lifetime }
75    }
76
77    /// Creates a new `LocalPooledInner` sharing the lifetime with an existing one.
78    ///
79    /// This is used for type casting operations where we want to create a new typed view
80    /// while sharing the same lifetime management.
81    #[must_use]
82    fn with_shared_lifetime(pooled: RawPooled<T>, lifetime: Rc<LocalPooledRef>) -> Self {
83        Self { pooled, lifetime }
84    }
85}
86
87impl<T: ?Sized> LocalPooled<T> {
88    /// Creates a new [`LocalPooled<T>`] from a pooled item and pool handle.
89    ///
90    /// This is an internal constructor used by [`LocalBlindPool::insert`] and for
91    /// reconstructing pooled values after type casting via the [`define_pooled_dyn_cast!`] macro.
92    #[must_use]
93    pub(crate) fn new(pooled: RawPooled<T>, pool: LocalBlindPool) -> Self {
94        let inner = LocalPooledInner::new(pooled, pool);
95        Self {
96            inner: Rc::new(inner),
97        }
98    }
99
100    /// Erases the type information from this [`LocalPooled<T>`] handle,
101    /// returning a [`LocalPooled<()>`].
102    ///
103    /// This is useful when you want to store handles of different types in the same collection
104    /// or pass them to code that doesn't need to know the specific type.
105    ///
106    /// The returned handle shares the same underlying reference count as the original handle.
107    /// Multiple handles (both typed and type-erased) can coexist for the same pooled item,
108    /// and the item will only be removed from the pool when all handles are dropped.
109    ///
110    /// # Example
111    ///
112    /// ```rust
113    /// use blind_pool::LocalBlindPool;
114    ///
115    /// let pool = LocalBlindPool::new();
116    /// let value_handle = pool.insert("Test".to_string());
117    /// let cloned_handle = value_handle.clone();
118    ///
119    /// // Erase type information while keeping the original handle via clone.
120    /// let erased = value_handle.erase();
121    ///
122    /// // Both handles are valid and refer to the same item.
123    /// assert_eq!(*cloned_handle, "Test".to_string());
124    ///
125    /// // The erased handle shares the same reference count.
126    /// drop(erased);
127    /// assert_eq!(*cloned_handle, "Test".to_string()); // Still accessible via typed handle.
128    /// ```
129    #[must_use]
130    #[inline]
131    pub fn erase(self) -> LocalPooled<()> {
132        // Create a new erased handle sharing the same lifetime manager
133        let erased_pooled = self.inner.pooled.erase();
134        let erased_inner =
135            LocalPooledInner::with_shared_lifetime(erased_pooled, Rc::clone(&self.inner.lifetime));
136
137        LocalPooled {
138            inner: Rc::new(erased_inner),
139        }
140    }
141
142    /// Returns a pointer to the stored value.
143    ///
144    /// This provides direct access to the underlying pointer while maintaining the safety
145    /// guarantees of the pooled reference. The pointer remains valid as long as any
146    /// [`LocalPooled<T>`] handle exists for the same value.
147    ///
148    /// # Example
149    ///
150    /// ```rust
151    /// use blind_pool::LocalBlindPool;
152    ///
153    /// let pool = LocalBlindPool::new();
154    /// let value_handle = pool.insert("Test".to_string());
155    ///
156    /// // Get the pointer to the stored value.
157    /// let ptr = value_handle.ptr();
158    ///
159    /// // SAFETY: The pointer is valid as long as value_handle exists.
160    /// let value = unsafe { ptr.as_ref() };
161    /// assert_eq!(value, "Test");
162    /// ```
163    #[must_use]
164    #[inline]
165    pub fn ptr(&self) -> std::ptr::NonNull<T> {
166        self.inner.pooled.ptr()
167    }
168
169    /// Returns a pinned reference to the value stored in the pool.
170    ///
171    /// Since values in the pool are always pinned (they never move once inserted),
172    /// this method provides safe access to `Pin<&T>` without requiring unsafe code.
173    ///
174    /// # Example
175    ///
176    /// ```rust
177    /// use std::pin::Pin;
178    ///
179    /// use blind_pool::LocalBlindPool;
180    ///
181    /// let pool = LocalBlindPool::new();
182    /// let handle = pool.insert("hello".to_string());
183    ///
184    /// let pinned: Pin<&String> = handle.as_pin();
185    /// assert_eq!(pinned.len(), 5);
186    /// ```
187    #[must_use]
188    #[inline]
189    pub fn as_pin(&self) -> Pin<&T> {
190        // Delegate to the underlying opaque_pool Pooled<T> as_pin implementation.
191        self.inner.pooled.opaque_handle().as_pin()
192    }
193
194    /// Casts this [`LocalPooled<T>`] to a trait object type.
195    ///
196    /// This method converts a pooled value from a concrete type to a trait object
197    /// while preserving the reference counting and pool management semantics.
198    ///
199    /// This method is only intended for use by the [`define_pooled_dyn_cast!`] macro.
200    #[doc(hidden)]
201    #[must_use]
202    #[inline]
203    pub fn __private_cast_dyn_with_fn<U: ?Sized, F>(self, cast_fn: F) -> LocalPooled<U>
204    where
205        F: FnOnce(&T) -> &U,
206    {
207        // Cast the RawPooled to the trait object using the provided function
208        // SAFETY: The lifetime management logic of this pool guarantees that the target item is
209        // still alive in the pool for as long as any handle exists, which it clearly does.
210        // We only ever hand out shared references to the item, so no conflicting `&mut`
211        // exclusive references can exist.
212        let cast_pooled = unsafe { self.inner.pooled.__private_cast_dyn_with_fn(cast_fn) };
213        let cast_inner =
214            LocalPooledInner::with_shared_lifetime(cast_pooled, Rc::clone(&self.inner.lifetime));
215
216        LocalPooled {
217            inner: Rc::new(cast_inner),
218        }
219    }
220}
221
222impl<T: ?Sized> Clone for LocalPooled<T> {
223    /// Creates another handle to the same pooled value.
224    ///
225    /// This increases the reference count for the underlying value. The value will only be
226    /// removed from the pool when all cloned handles are dropped.
227    ///
228    /// # Example
229    ///
230    /// ```rust
231    /// use blind_pool::LocalBlindPool;
232    ///
233    /// let pool = LocalBlindPool::new();
234    /// let value_handle = pool.insert("Test".to_string());
235    ///
236    /// let cloned_handle = value_handle.clone();
237    ///
238    /// // Both handles refer to the same value.
239    /// assert_eq!(*value_handle, *cloned_handle);
240    ///
241    /// // Value remains in pool until all handles are dropped.
242    /// drop(value_handle);
243    /// assert_eq!(*cloned_handle, "Test".to_string()); // Still accessible.
244    /// ```
245    #[inline]
246    fn clone(&self) -> Self {
247        Self {
248            inner: Rc::clone(&self.inner),
249        }
250    }
251}
252
253impl<T: ?Sized> Deref for LocalPooled<T> {
254    type Target = T;
255
256    /// Provides direct access to the value stored in the pool.
257    ///
258    /// This allows the handle to be used as if it were a reference to the stored value.
259    ///
260    /// # Example
261    ///
262    /// ```rust
263    /// use blind_pool::LocalBlindPool;
264    ///
265    /// let pool = LocalBlindPool::new();
266    /// let string_handle = pool.insert("hello".to_string());
267    ///
268    /// // Access string methods directly.
269    /// assert_eq!(string_handle.len(), 5);
270    /// assert!(string_handle.starts_with("he"));
271    /// ```
272    #[inline]
273    fn deref(&self) -> &Self::Target {
274        // Delegate to the underlying opaque_pool Pooled<T> Deref implementation.
275        self.inner.pooled.opaque_handle()
276    }
277}
278
279impl Drop for LocalPooledRef {
280    /// Automatically removes the item from the pool when the last reference is dropped.
281    ///
282    /// This ensures that resources are properly cleaned up without requiring manual intervention.
283    #[inline]
284    fn drop(&mut self) {
285        // We are guaranteed to be the only one executing this drop because Rc ensures
286        // that Drop on the LocalPooledRef is only called once when the last reference is released.
287        // SAFETY: This pooled handle is being consumed by Drop, ensuring it cannot be used again.
288        unsafe {
289            self.pool.remove(&self.pooled);
290        }
291    }
292}
293
294impl<T: ?Sized> fmt::Debug for LocalPooled<T> {
295    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296        f.debug_struct("LocalPooled")
297            .field("type_name", &std::any::type_name::<T>())
298            .field("ptr", &self.inner.pooled.ptr())
299            .finish()
300    }
301}
302
303impl<T: ?Sized> fmt::Debug for LocalPooledInner<T> {
304    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305        f.debug_struct("LocalPooledInner")
306            .field("type_name", &std::any::type_name::<T>())
307            .field("ptr", &self.pooled.ptr())
308            .field("lifetime", &self.lifetime)
309            .finish()
310    }
311}
312
313impl fmt::Debug for LocalPooledRef {
314    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315        f.debug_struct("LocalPooledRef")
316            .field("pooled", &self.pooled)
317            .field("pool", &self.pool)
318            .finish()
319    }
320}
321
322#[cfg(test)]
323mod tests {
324    use static_assertions::assert_not_impl_any;
325
326    use super::LocalPooled;
327    use crate::LocalBlindPool;
328
329    #[test]
330    fn single_threaded_assertions() {
331        // LocalPooled<T> should NOT be Send or Sync regardless of T's Send/Sync status
332        // because it uses Rc internally which is not Send/Sync
333        assert_not_impl_any!(LocalPooled<u32>: Send);
334        assert_not_impl_any!(LocalPooled<u32>: Sync);
335        assert_not_impl_any!(LocalPooled<String>: Send);
336        assert_not_impl_any!(LocalPooled<String>: Sync);
337        assert_not_impl_any!(LocalPooled<Vec<u8>>: Send);
338        assert_not_impl_any!(LocalPooled<Vec<u8>>: Sync);
339
340        // Even with non-Send/non-Sync types, LocalPooled should still not be Send/Sync
341        use std::rc::Rc;
342        assert_not_impl_any!(LocalPooled<Rc<u32>>: Send);
343        assert_not_impl_any!(LocalPooled<Rc<u32>>: Sync);
344
345        use std::cell::RefCell;
346        assert_not_impl_any!(LocalPooled<RefCell<u32>>: Send);
347        assert_not_impl_any!(LocalPooled<RefCell<u32>>: Sync);
348
349        // LocalPooled<T> should always be Unpin regardless of T
350        use static_assertions::assert_impl_all;
351        assert_impl_all!(LocalPooled<u32>: Unpin);
352        assert_impl_all!(LocalPooled<String>: Unpin);
353        assert_impl_all!(LocalPooled<Vec<u8>>: Unpin);
354        assert_impl_all!(LocalPooled<RefCell<u32>>: Unpin);
355        assert_impl_all!(LocalPooled<Rc<u32>>: Unpin);
356
357        // Even with non-Unpin types, LocalPooled should still be Unpin
358        use std::marker::PhantomPinned;
359        assert_impl_all!(LocalPooled<PhantomPinned>: Unpin);
360    }
361
362    #[test]
363    fn automatic_cleanup_single_handle() {
364        let pool = LocalBlindPool::new();
365
366        {
367            let _u32_handle = pool.insert(42_u32);
368            assert_eq!(pool.len(), 1);
369        }
370
371        // Item should be automatically removed after drop
372        assert_eq!(pool.len(), 0);
373        assert!(pool.is_empty());
374    }
375
376    #[test]
377    fn automatic_cleanup_multiple_handles() {
378        let pool = LocalBlindPool::new();
379
380        let u32_handle = pool.insert(42_u32);
381        let cloned_handle = u32_handle.clone();
382
383        assert_eq!(pool.len(), 1);
384
385        // Drop first handle - item should remain
386        drop(u32_handle);
387        assert_eq!(pool.len(), 1);
388
389        // Drop second handle - item should be removed
390        drop(cloned_handle);
391        assert_eq!(pool.len(), 0);
392    }
393
394    #[test]
395    fn clone_handles() {
396        let pool = LocalBlindPool::new();
397
398        let value_handle = pool.insert(42_u64);
399        let cloned_handle = value_handle.clone();
400
401        // Both handles should refer to the same value
402        assert_eq!(*value_handle, 42);
403        assert_eq!(*cloned_handle, 42);
404
405        // Modification through one handle should be visible through the other
406        // (Note: we can't actually modify since we only have shared references)
407        assert_eq!(*value_handle, *cloned_handle);
408    }
409
410    #[test]
411    fn string_methods_through_deref() {
412        let pool = LocalBlindPool::new();
413
414        let string_handle = pool.insert("hello world".to_string());
415
416        // Test that we can call String methods directly
417        assert_eq!(string_handle.len(), 11);
418        assert!(string_handle.starts_with("hello"));
419        assert!(string_handle.ends_with("world"));
420        assert!(string_handle.contains("lo wo"));
421    }
422
423    #[test]
424    fn ptr_access() {
425        let pool = LocalBlindPool::new();
426
427        let value_handle = pool.insert(42_u64);
428
429        // Access the value directly through dereferencing
430        assert_eq!(*value_handle, 42);
431
432        // Access the value through the ptr() method
433        let ptr = value_handle.ptr();
434        // SAFETY: The pointer is valid as long as value_handle exists.
435        let value = unsafe { ptr.read() };
436        assert_eq!(value, 42);
437    }
438
439    #[test]
440    fn erase_type_information() {
441        let pool = LocalBlindPool::new();
442
443        let u64_handle = pool.insert(42_u64);
444        let typed_clone = u64_handle.clone();
445        let erased = u64_handle.erase();
446
447        // Verify the typed handle still works
448        assert_eq!(*typed_clone, 42);
449
450        // Pool should still contain the item
451        assert_eq!(pool.len(), 1);
452
453        drop(erased);
454        drop(typed_clone);
455        assert_eq!(pool.len(), 0);
456    }
457
458    #[test]
459    fn erase_with_multiple_references_works() {
460        let pool = LocalBlindPool::new();
461
462        let value_handle = pool.insert(42_u64);
463        let cloned_handle = value_handle.clone();
464
465        // This should now work without panicking
466        let erased = value_handle.erase();
467
468        // Both handles should still work
469        assert_eq!(*cloned_handle, 42);
470
471        // Verify the erased handle is valid by ensuring cleanup works properly
472        drop(erased);
473        assert_eq!(*cloned_handle, 42); // Typed handle should still work
474
475        drop(cloned_handle);
476        assert_eq!(pool.len(), 0);
477    }
478
479    #[test]
480    fn drop_with_types_that_have_drop() {
481        let pool = LocalBlindPool::new();
482
483        // Vec has a non-trivial Drop implementation
484        let vec_handle = pool.insert(vec![1, 2, 3, 4, 5]);
485
486        assert_eq!(vec_handle.len(), 5);
487        assert_eq!(pool.len(), 1);
488
489        drop(vec_handle);
490        assert_eq!(pool.len(), 0);
491    }
492
493    #[test]
494    fn works_with_single_byte_type() {
495        let pool = LocalBlindPool::new();
496
497        let u8_handle = pool.insert(255_u8);
498
499        assert_eq!(*u8_handle, 255);
500        assert_eq!(pool.len(), 1);
501
502        drop(u8_handle);
503        assert_eq!(pool.len(), 0);
504    }
505}