use core::mem;
use core::ptr::{self, NonNull};
use allocator_api2::alloc::{AllocError, Allocator};
use super::alloc_prefixed::PREFIX_BYTES;
use super::alloc_value::acquire_shared_chunk_ref;
use super::{Arena, ExpectAlloc};
use crate::strings::{ArcUtf16Str, BoxUtf16Str, Utf16String};
#[cfg_attr(docsrs, doc(cfg(feature = "utf16")))]
impl<A: Allocator + Clone> Arena<A> {
#[must_use]
#[inline]
pub fn alloc_utf16_str_arc(&self, s: impl AsRef<widestring::Utf16Str>) -> ArcUtf16Str<A>
where
A: Send + Sync,
{
self.try_alloc_utf16_str_arc(s).expect_alloc()
}
#[inline]
pub fn try_alloc_utf16_str_arc(&self, s: impl AsRef<widestring::Utf16Str>) -> Result<ArcUtf16Str<A>, AllocError>
where
A: Send + Sync,
{
self.impl_alloc_prefixed_shared_arc::<u16>(s.as_ref().as_slice()).map(|ptr|
unsafe { ArcUtf16Str::from_raw(ptr) })
}
#[must_use]
#[inline]
pub fn alloc_utf16_str_box(&self, s: impl AsRef<widestring::Utf16Str>) -> BoxUtf16Str<A> {
self.try_alloc_utf16_str_box(s).expect_alloc()
}
#[inline]
pub fn try_alloc_utf16_str_box(&self, s: impl AsRef<widestring::Utf16Str>) -> Result<BoxUtf16Str<A>, AllocError> {
self.impl_alloc_prefixed_shared::<u16>(s.as_ref().as_slice()).map(|ptr|
unsafe { BoxUtf16Str::from_raw(ptr) })
}
#[must_use]
#[inline]
pub fn alloc_utf16_str_arc_from_str(&self, s: impl AsRef<str>) -> ArcUtf16Str<A>
where
A: Send + Sync,
{
self.try_alloc_utf16_str_arc_from_str(s).expect_alloc()
}
#[inline]
pub fn try_alloc_utf16_str_arc_from_str(&self, s: impl AsRef<str>) -> Result<ArcUtf16Str<A>, AllocError>
where
A: Send + Sync,
{
self.impl_alloc_utf16_prefixed_from_str_arc(s.as_ref()).map(|ptr|
unsafe { ArcUtf16Str::from_raw(ptr) })
}
#[must_use]
#[inline]
pub fn alloc_utf16_str_box_from_str(&self, s: impl AsRef<str>) -> BoxUtf16Str<A> {
self.try_alloc_utf16_str_box_from_str(s).expect_alloc()
}
#[inline]
pub fn try_alloc_utf16_str_box_from_str(&self, s: impl AsRef<str>) -> Result<BoxUtf16Str<A>, AllocError> {
self.impl_alloc_utf16_prefixed_from_str(s.as_ref()).map(|ptr|
unsafe { BoxUtf16Str::from_raw(ptr) })
}
#[must_use]
#[inline]
pub const fn alloc_utf16_string(&self) -> Utf16String<'_, A> {
Utf16String::new_in(self)
}
#[must_use]
#[inline]
pub fn alloc_utf16_string_with_capacity(&self, cap: usize) -> Utf16String<'_, A> {
Utf16String::with_capacity_in(cap, self)
}
#[inline]
pub fn try_alloc_utf16_string_with_capacity(&self, cap: usize) -> Result<Utf16String<'_, A>, AllocError> {
Utf16String::try_with_capacity_in(cap, self)
}
#[inline(always)]
#[cfg_attr(test, mutants::skip)] #[allow(
clippy::cast_ptr_alignment,
reason = "base reservation is align_of::<u16>() (passed to try_alloc); `base + PREFIX_BYTES` is u16-aligned"
)]
fn impl_alloc_utf16_prefixed_from_str(&self, s: &str) -> Result<NonNull<u16>, AllocError> {
let exact: usize = s.chars().map(char::len_utf16).sum();
let elem_size = mem::size_of::<u16>();
let elem_align = mem::align_of::<u16>();
let payload_bytes = exact.checked_mul(elem_size).ok_or(AllocError)?.max(elem_align);
let total = PREFIX_BYTES.checked_add(payload_bytes).ok_or(AllocError)?;
loop {
if let Some((uninit, chunk_ptr)) = self.current_shared().try_alloc_with_chunk(total, elem_align) {
let chunk_ref = self.acquire_current_shared_chunk_ref(chunk_ptr);
let payload = transcode_utf16_into(uninit.as_non_null(), s, exact);
let _ = chunk_ref.forget();
return Ok(payload);
}
if self.is_oversized(total) {
return self.alloc_oversized_shared_with(total, |mutator, chunk_ptr| {
let (base, _chunk_unused) = mutator
.try_alloc_with_chunk(total, elem_align)
.expect("dedicated oversized chunk sized to fit utf-16 payload");
let chunk_ref = acquire_shared_chunk_ref::<A>(chunk_ptr);
let payload = transcode_utf16_into(base.as_non_null(), s, exact);
let _ = chunk_ref.forget();
payload
});
}
self.refill_shared(total)?;
}
}
#[inline(always)]
#[cfg_attr(test, mutants::skip)] fn impl_alloc_utf16_prefixed_from_str_arc(&self, s: &str) -> Result<NonNull<u16>, AllocError> {
let exact: usize = s.chars().map(char::len_utf16).sum();
let bytes_needed = super::alloc_prefixed::worst_case_arc_slice_payload::<u16>(exact);
loop {
if let Some((uninit, chunk_ptr)) = self.try_reserve_arc_slice::<u16>(exact) {
let chunk_ref = self.acquire_current_shared_chunk_ref(chunk_ptr);
let payload = uninit.init_from_iter_ptr(s.encode_utf16());
let _ = chunk_ref.forget();
return Ok(payload.cast::<u16>());
}
if self.is_oversized(bytes_needed) {
return self.alloc_oversized_shared_with(bytes_needed, |mutator, chunk_ptr| {
let (ticket, _chunk) = mutator
.try_alloc_arc_slice::<u16>(exact)
.expect("dedicated oversized chunk sized to fit utf-16 Arc payload");
let chunk_ref = acquire_shared_chunk_ref::<A>(chunk_ptr);
let payload = ticket.init_from_iter_ptr(s.encode_utf16());
let _ = chunk_ref.forget();
payload.cast::<u16>()
});
}
self.refill_shared(bytes_needed)?;
}
}
}
#[cfg_attr(not(coverage_nightly), inline(always))]
#[allow(
clippy::cast_ptr_alignment,
reason = "see callers: `base + PREFIX_BYTES` is u16-aligned by construction"
)]
fn transcode_utf16_into(base: NonNull<u8>, s: &str, exact: usize) -> NonNull<u16> {
unsafe {
ptr::write_unaligned(base.as_ptr().cast::<usize>(), exact);
let payload_ptr = base.as_ptr().add(PREFIX_BYTES).cast::<u16>();
let mut written = 0_usize;
for u in s.encode_utf16() {
debug_assert!(written < exact, "transcoded count exceeded pre-computed length");
payload_ptr.add(written).write(u);
written += 1;
}
debug_assert_eq!(written, exact, "transcoded count differed from pre-computed length");
NonNull::new_unchecked(payload_ptr)
}
}