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}