osom_lib_strings/immutable/
immutable_string.rs1use core::{
2 borrow::Borrow,
3 hash::Hash,
4 sync::atomic::{Ordering, fence},
5};
6
7use osom_lib_alloc::traits::Allocator;
8use osom_lib_primitives::length::Length;
9use osom_lib_reprc::macros::reprc;
10
11use crate::immutable::{ImmutableStringError, WeakImmutableString, internal_string::InternalString};
12
13#[reprc]
16#[repr(transparent)]
17#[derive(Debug)]
18pub struct ImmutableString<TAllocator: Allocator> {
19 internal: InternalString<TAllocator>,
20}
21
22impl<TAllocator: Allocator> ImmutableString<TAllocator> {
23 #[inline(always)]
24 pub(crate) fn from_internal(internal: InternalString<TAllocator>) -> Self {
25 Self { internal }
26 }
27
28 #[inline(always)]
36 pub fn empty() -> Result<Self, ImmutableStringError> {
37 Self::empty_with_allocator(TAllocator::default())
38 }
39
40 #[inline(always)]
49 pub fn from_str_slice(text: &str) -> Result<Self, ImmutableStringError> {
50 Self::from_str_slice_and_allocator(text, TAllocator::default())
51 }
52
53 #[inline]
62 pub fn from_str_slice_and_allocator(text: &str, allocator: TAllocator) -> Result<Self, ImmutableStringError> {
63 let text_len = Length::try_from_usize(text.len())?;
64 let mut builder = super::ImmutableStringBuilder::with_capacity_and_allocator(text_len, allocator)?;
65 builder.try_push(text)?;
66 let result = builder.build()?;
67 Ok(result)
68 }
69
70 #[inline(always)]
78 pub fn empty_with_allocator(allocator: TAllocator) -> Result<Self, ImmutableStringError> {
79 let internal = InternalString::with_allocator_and_capacity(Length::ZERO, allocator)?;
80 let result = Self::from_internal(internal);
81 Ok(result)
82 }
83
84 #[inline(always)]
85 pub fn strong_count(&self) -> u32 {
86 self.internal.strong().load(Ordering::Relaxed)
87 }
88
89 #[inline(always)]
90 pub fn weak_count(&self) -> u32 {
91 self.internal.weak().load(Ordering::Relaxed)
92 }
93
94 #[inline]
96 pub const fn as_str(&self) -> &str {
97 unsafe {
98 let slice = core::slice::from_raw_parts(self.internal.data_start(), self.internal.length().as_usize() - 1);
99 core::str::from_utf8_unchecked(slice)
100 }
101 }
102
103 #[inline]
109 pub const fn as_c_str(&self) -> &str {
110 unsafe {
111 let slice = core::slice::from_raw_parts(self.internal.data_start(), self.internal.length().as_usize());
112 core::str::from_utf8_unchecked(slice)
113 }
114 }
115
116 #[inline]
118 pub const fn length(&self) -> Length {
119 unsafe { Length::new_unchecked(self.internal.length().as_u32() - 1) }
120 }
121
122 #[inline]
124 pub fn downgrade(&self) -> WeakImmutableString<TAllocator> {
125 let internal_clone = self.internal.clone();
126 internal_clone.weak().fetch_add(1, Ordering::Relaxed);
127 WeakImmutableString::from_internal(internal_clone)
128 }
129
130 #[inline]
136 pub fn abandon(mut self) -> Option<WeakImmutableString<TAllocator>> {
137 let result = self.internal_abandon();
138 core::mem::forget(self);
139 result
140 }
141
142 fn internal_abandon(&mut self) -> Option<WeakImmutableString<TAllocator>> {
143 let internal = unsafe { core::ptr::read(&raw const self.internal) };
144 let prev = internal.strong().fetch_sub(1, Ordering::Release);
145 if prev > 1 {
146 return None;
147 }
148
149 fence(Ordering::Acquire);
151 Some(WeakImmutableString::from_internal(internal))
152 }
153}
154
155impl<TAllocator: Allocator> Drop for ImmutableString<TAllocator> {
156 fn drop(&mut self) {
157 let _ = self.internal_abandon();
158 }
159}
160
161impl<TAllocator: Allocator> Clone for ImmutableString<TAllocator> {
162 fn clone(&self) -> Self {
163 let internal_clone = self.internal.clone();
164 internal_clone.strong().fetch_add(1, Ordering::Relaxed);
165 Self {
166 internal: internal_clone,
167 }
168 }
169}
170
171impl<TAllocator: Allocator> AsRef<str> for ImmutableString<TAllocator> {
172 fn as_ref(&self) -> &str {
173 self.as_str()
174 }
175}
176
177impl<TAllocator: Allocator> Borrow<str> for ImmutableString<TAllocator> {
178 fn borrow(&self) -> &str {
179 self.as_str()
180 }
181}
182
183impl<TAllocator: Allocator> Hash for ImmutableString<TAllocator> {
184 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
185 self.as_str().hash(state);
186 }
187}
188
189impl<TAllocator: Allocator, TRight: AsRef<str>> PartialEq<TRight> for ImmutableString<TAllocator> {
190 fn eq(&self, other: &TRight) -> bool {
191 self.as_str() == other.as_ref()
192 }
193}
194
195impl<TAllocator: Allocator> Eq for ImmutableString<TAllocator> {}