use core::{
borrow::Borrow,
fmt::Display,
hash::Hash,
sync::atomic::{Ordering, fence},
};
use osom_lib_alloc::traits::Allocator;
use osom_lib_primitives::length::Length;
use osom_lib_reprc::macros::reprc;
use osom_lib_try_clone::TryClone;
use crate::immutable::{
ImmutableStringError, MAX_REFERENCES, MaxReferencesExceededError, WeakImmutableString,
internal_string::InternalString,
};
#[reprc]
#[repr(transparent)]
#[derive(Debug)]
#[must_use]
pub struct ImmutableString<TAllocator: Allocator> {
internal: InternalString<TAllocator>,
}
impl<TAllocator: Allocator> ImmutableString<TAllocator> {
#[inline(always)]
pub(crate) fn from_internal(internal: InternalString<TAllocator>) -> Self {
Self { internal }
}
#[inline(always)]
pub fn empty() -> Result<Self, ImmutableStringError> {
Self::empty_with_allocator(TAllocator::default())
}
#[inline(always)]
pub fn from_str_slice(text: &str) -> Result<Self, ImmutableStringError> {
Self::from_str_slice_and_allocator(text, TAllocator::default())
}
#[inline]
pub fn from_str_slice_and_allocator(text: &str, allocator: TAllocator) -> Result<Self, ImmutableStringError> {
let text_len = Length::try_from_usize(text.len())?;
let mut builder = super::ImmutableStringBuilder::with_capacity_and_allocator(text_len, allocator)?;
builder.try_push(text)?;
let result = builder.build()?;
Ok(result)
}
#[inline(always)]
pub fn empty_with_allocator(allocator: TAllocator) -> Result<Self, ImmutableStringError> {
Self::from_str_slice_and_allocator("", allocator)
}
#[inline(always)]
#[must_use]
pub fn strong_count(&self) -> u32 {
self.internal.strong().load(Ordering::Relaxed)
}
#[inline(always)]
#[must_use]
pub fn weak_count(&self) -> u32 {
self.internal.weak().load(Ordering::Relaxed)
}
#[inline]
#[must_use]
pub fn ptr_eq(&self, other: &Self) -> bool {
core::ptr::eq(self.internal.raw_data(), other.internal.raw_data())
}
#[inline]
#[must_use]
pub const fn as_str(&self) -> &str {
unsafe {
let slice = core::slice::from_raw_parts(self.internal.data_start(), self.internal.length().as_usize() - 1);
core::str::from_utf8_unchecked(slice)
}
}
#[inline]
#[must_use]
pub const fn as_c_str(&self) -> &str {
unsafe {
let slice = core::slice::from_raw_parts(self.internal.data_start(), self.internal.length().as_usize());
core::str::from_utf8_unchecked(slice)
}
}
#[inline]
pub const fn length(&self) -> Length {
unsafe { Length::new_unchecked(self.internal.length().as_u32() - 1) }
}
pub fn downgrade(&self) -> Result<WeakImmutableString<TAllocator>, MaxReferencesExceededError> {
let internal_clone = self.internal.clone();
let prev_value = internal_clone.weak().fetch_add(1, Ordering::Relaxed);
if prev_value >= MAX_REFERENCES {
internal_clone.weak().fetch_sub(1, Ordering::Relaxed);
return Err(MaxReferencesExceededError);
}
Ok(WeakImmutableString::from_internal(internal_clone))
}
#[inline]
#[must_use]
pub fn abandon(mut self) -> Option<WeakImmutableString<TAllocator>> {
let result = self.internal_abandon();
core::mem::forget(self);
result
}
fn internal_abandon(&mut self) -> Option<WeakImmutableString<TAllocator>> {
let internal = unsafe { core::ptr::read(&raw const self.internal) };
let prev = internal.strong().fetch_sub(1, Ordering::Release);
if prev > 1 {
return None;
}
fence(Ordering::Acquire);
Some(WeakImmutableString::from_internal(internal))
}
}
impl<TAllocator: Allocator> Drop for ImmutableString<TAllocator> {
fn drop(&mut self) {
let _ = self.internal_abandon();
}
}
impl<TAllocator: Allocator> TryClone for ImmutableString<TAllocator> {
type Error = MaxReferencesExceededError;
fn try_clone(&self) -> Result<Self, Self::Error> {
let internal_clone = self.internal.clone();
let prev_value = internal_clone.strong().fetch_add(1, Ordering::Relaxed);
if prev_value >= MAX_REFERENCES {
internal_clone.strong().fetch_sub(1, Ordering::Relaxed);
return Err(MaxReferencesExceededError);
}
Ok(Self {
internal: internal_clone,
})
}
}
impl<TAllocator: Allocator> Clone for ImmutableString<TAllocator> {
fn clone(&self) -> Self {
self.try_clone()
.expect("ImmutableString strong reference count is too high.")
}
}
impl<TAllocator: Allocator> AsRef<str> for ImmutableString<TAllocator> {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<TAllocator: Allocator> Borrow<str> for ImmutableString<TAllocator> {
fn borrow(&self) -> &str {
self.as_str()
}
}
impl<TAllocator: Allocator> Hash for ImmutableString<TAllocator> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.as_str().hash(state);
}
}
impl<TAllocator: Allocator> PartialEq for ImmutableString<TAllocator> {
fn eq(&self, other: &Self) -> bool {
self.internal.raw_equals(&other.internal) || self.as_str() == other.as_ref()
}
}
impl<TAllocator: Allocator> Eq for ImmutableString<TAllocator> {}
impl<TAllocator: Allocator> Display for ImmutableString<TAllocator> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.as_str())
}
}