use crate::iter::CodePointsIter;
use crate::vtable::JsStringVTable;
use crate::{JsStr, JsString, JsStringKind};
use std::cell::Cell;
use std::process::abort;
use std::ptr::NonNull;
#[repr(C)]
pub(crate) struct SliceString {
vtable: JsStringVTable,
owned: JsString,
inner: JsStr<'static>,
refcount: Cell<usize>,
}
impl SliceString {
#[inline]
#[must_use]
pub(crate) unsafe fn new(owned: &JsString, start: usize, end: usize) -> Self {
let inner = unsafe { owned.as_str().get_unchecked(start..end) };
SliceString {
vtable: JsStringVTable {
clone: slice_clone,
drop: slice_drop,
as_str: slice_as_str,
code_points: slice_code_points,
refcount: slice_refcount,
len: end - start,
kind: JsStringKind::Slice,
},
owned: owned.clone(),
inner: unsafe { inner.as_static() },
refcount: Cell::new(1),
}
}
#[inline]
#[must_use]
pub(crate) fn owned(&self) -> &JsString {
&self.owned
}
}
#[inline]
pub(super) fn slice_clone(vtable: NonNull<JsStringVTable>) -> JsString {
let this: &SliceString = unsafe { vtable.cast().as_ref() };
let Some(strong) = this.refcount.get().checked_add(1) else {
abort();
};
this.refcount.set(strong);
unsafe { JsString::from_ptr(vtable) }
}
#[inline]
fn slice_drop(vtable: NonNull<JsStringVTable>) {
let this: &SliceString = unsafe { vtable.cast().as_ref() };
let Some(new) = this.refcount.get().checked_sub(1) else {
abort();
};
this.refcount.set(new);
if new != 0 {
return;
}
unsafe {
drop(Box::from_raw(vtable.cast::<SliceString>().as_ptr()));
}
}
#[inline]
fn slice_as_str(vtable: NonNull<JsStringVTable>) -> JsStr<'static> {
let this: &SliceString = unsafe { vtable.cast().as_ref() };
this.inner
}
#[inline]
fn slice_code_points(vtable: NonNull<JsStringVTable>) -> CodePointsIter<'static> {
CodePointsIter::new(slice_as_str(vtable))
}
#[inline]
#[allow(clippy::unnecessary_wraps)]
fn slice_refcount(vtable: NonNull<JsStringVTable>) -> Option<usize> {
let this: &SliceString = unsafe { vtable.cast().as_ref() };
Some(this.refcount.get())
}