Skip to main content

osom_lib_strings/immutable/
weak_string.rs

1use osom_lib_alloc::traits::Allocator;
2use osom_lib_arc::carc_array::CWeakArray;
3use osom_lib_reprc::macros::reprc;
4use osom_lib_try_clone::TryClone;
5
6use crate::immutable::{ImmutableString, MaxReferencesExceededError};
7
8/// Represents possible errors when upgrading a [`WeakImmutableString`] to a [`ImmutableString`].
9#[reprc]
10#[repr(u8)]
11#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
12#[must_use]
13pub enum WeakUpgradeError {
14    /// The maximum number of references is exceeded.
15    MaxReferencesExceeded = 0,
16
17    /// There are no strong references alive.
18    NoStrongReferencesAlive = 1,
19}
20
21impl From<osom_lib_arc::errors::WeakUpgradeError> for WeakUpgradeError {
22    fn from(err: osom_lib_arc::errors::WeakUpgradeError) -> Self {
23        match err {
24            osom_lib_arc::errors::WeakUpgradeError::MaxReferencesExceeded => Self::MaxReferencesExceeded,
25            osom_lib_arc::errors::WeakUpgradeError::NoStrongReferencesAlive => Self::NoStrongReferencesAlive,
26        }
27    }
28}
29
30/// A weak reference to the underlying [`ImmutableString`].
31///
32/// This object cannot inspect the underlying string. But it does track
33/// weak references, and each weak reference can build a strong reference,
34/// assuming any other strong reference is alive.
35///
36/// # Examples
37///
38/// ```rust
39/// # cfg_select! {
40/// #    feature="std" => {
41/// use osom_lib_strings::immutable::{ImmutableStringError, WeakUpgradeError};
42/// use osom_lib_strings::immutable::std::StdImmutableString;
43///
44/// fn main() -> Result<(), ImmutableStringError> {
45///     // The first instance counts as both strong and weak reference.
46///     let strong1 = StdImmutableString::from_str_slice("foo")?;
47///     assert_eq!(strong1.strong_count(), 1);
48///     assert_eq!(strong1.weak_count(), 1);
49///     assert_eq!(strong1.as_str(), "foo");
50///
51///     let weak = strong1.downgrade().unwrap();  // Cheap operation
52///     assert_eq!(strong1.strong_count(), 1);
53///     assert_eq!(strong1.weak_count(), 2);
54///     assert_eq!(weak.strong_count(), 1);
55///     assert_eq!(weak.weak_count(), 2);
56///
57///     let strong2 = weak.upgrade().unwrap();  // Cheap operation
58///     assert_eq!(strong1.strong_count(), 2);
59///     assert_eq!(strong1.weak_count(), 2);
60///     assert_eq!(weak.strong_count(), 2);
61///     assert_eq!(weak.weak_count(), 2);
62///     assert_eq!(strong2.as_str(), strong1.as_str());
63///
64///     drop(strong2);
65///     assert_eq!(strong1.strong_count(), 1);
66///     assert_eq!(strong1.weak_count(), 2);
67///     assert_eq!(weak.strong_count(), 1);
68///     assert_eq!(weak.weak_count(), 2);
69///
70///     drop(strong1);
71///     // Both counters are decreased, since there are no more strong references.
72///     assert_eq!(weak.strong_count(), 0);
73///     assert_eq!(weak.weak_count(), 1);
74///
75///     // No more strong reference, .upgrade() won't work.
76///     assert!(matches!(weak.upgrade(), Err(WeakUpgradeError::NoStrongReferencesAlive)));
77///
78///     drop(weak);  // The actual deallocation happens here.
79///     Ok(())
80/// }
81/// #   },
82/// #   _ => { }
83/// # }
84/// ```
85#[reprc]
86#[repr(transparent)]
87#[derive(Debug)]
88#[must_use]
89pub struct WeakImmutableString<TAllocator: Allocator> {
90    internal: CWeakArray<u8, TAllocator>,
91}
92
93impl<TAllocator: Allocator> WeakImmutableString<TAllocator> {
94    #[inline(always)]
95    pub(crate) fn from_internal(internal: CWeakArray<u8, TAllocator>) -> Self {
96        Self { internal }
97    }
98
99    /// Returns the number of strong references to the [`WeakImmutableString`].
100    #[inline(always)]
101    #[must_use]
102    pub fn strong_count(&self) -> u32 {
103        CWeakArray::strong_count(&self.internal)
104    }
105
106    /// Returns the number of weak references to the [`WeakImmutableString`].
107    #[inline(always)]
108    #[must_use]
109    pub fn weak_count(&self) -> u32 {
110        CWeakArray::weak_count(&self.internal)
111    }
112
113    /// Returns the underlying string.
114    ///
115    /// # Notes
116    ///
117    /// Even though this is a weak reference, the string is still valid,
118    /// because a sequence of chars is not `Drop`.
119    #[inline]
120    #[must_use]
121    pub fn as_str(&self) -> &str {
122        unsafe {
123            let slice = self.internal.data();
124            core::str::from_utf8_unchecked(&slice[..slice.len() - 1])
125        }
126    }
127
128    /// Returns the underlying string as C-string.
129    ///
130    /// # Notes
131    ///
132    /// Even though this is a weak reference, the string is still valid,
133    /// because a sequence of chars is not `Drop`.
134    #[inline]
135    #[must_use]
136    pub fn as_c_str(&self) -> &str {
137        unsafe {
138            let slice = self.internal.data();
139            core::str::from_utf8_unchecked(slice)
140        }
141    }
142
143    /// Upgrades current weak reference to the strong [`ImmutableString`].
144    ///
145    ///
146    /// # Errors
147    ///
148    /// For details see [`WeakUpgradeError`].
149    pub fn upgrade(&self) -> Result<ImmutableString<TAllocator>, WeakUpgradeError> {
150        let strong = CWeakArray::upgrade(&self.internal)?;
151        Ok(ImmutableString::from_internal(strong))
152    }
153
154    /// Abandons current weak reference.
155    ///
156    /// If the internal weak counter is positive it returns false.
157    ///
158    /// Otherwise it deallocates the underlying memory and returns true.
159    /// In particular only single (the last) [`WeakImmutableString`] returns true
160    /// by calling this.
161    #[inline(always)]
162    #[must_use]
163    pub fn abandon(self) -> bool {
164        CWeakArray::<u8, TAllocator>::abandon(self.internal)
165    }
166}
167
168impl<TAllocator: Allocator> TryClone for WeakImmutableString<TAllocator> {
169    type Error = MaxReferencesExceededError;
170
171    fn try_clone(&self) -> Result<Self, Self::Error> {
172        let internal = self.internal.try_clone()?;
173        Ok(Self { internal })
174    }
175}
176
177impl<TAllocator: Allocator> Clone for WeakImmutableString<TAllocator> {
178    fn clone(&self) -> Self {
179        self.try_clone()
180            .expect("WeakImmutableString weak reference count is too high.")
181    }
182}