use core::{
alloc::Layout,
borrow::{Borrow, BorrowMut},
cmp,
ffi::c_char,
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
mem::{self, MaybeUninit},
ops,
ptr::{self, NonNull},
slice,
};
use crate::{AllocError, SeaStr, _alloc::Allocator};
#[cfg(feature = "libc")]
#[cfg_attr(docsrs, doc(cfg(feature = "libc")))]
pub type SeaString = SeaStringIn<crate::_alloc::Libc>;
#[repr(transparent)]
pub struct SeaStringIn<A: Allocator> {
ptr: NonNull<u8>,
alloc: PhantomData<A>,
}
impl<A: Allocator> SeaStringIn<A> {
pub unsafe fn from_ptr(ptr: *mut c_char) -> Self {
Self {
ptr: NonNull::new_unchecked(ptr.cast()),
alloc: PhantomData,
}
}
pub fn into_raw(self) -> *mut c_char {
let ptr = self.ptr.as_ptr().cast();
mem::forget(self);
ptr
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn new(src: &core::ffi::CStr) -> Self {
Self::from_bytes(src.to_bytes())
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn from_bytes(src: &[u8]) -> Self {
match Self::try_of_bytes(src) {
Ok(it) => it,
Err(e) => e.handle(),
}
}
pub fn try_of_bytes(src: &[u8]) -> Result<Self, AllocError> {
unsafe {
Self::try_with_uninit(src.len(), |dst| {
debug_assert_eq!(src.len(), dst.len());
ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr().cast::<u8>(), dst.len());
})
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn with(len: usize, f: impl FnOnce(&mut [u8])) -> Self {
match Self::try_with(len, f) {
Ok(it) => it,
Err(e) => e.handle(),
}
}
pub fn try_with(len: usize, f: impl FnOnce(&mut [u8])) -> Result<Self, AllocError> {
assert_ne!(len, isize::MAX as usize);
unsafe {
Self::try_with_uninit(len, |it| {
let ptr = it.as_mut_ptr();
let len = it.len();
ptr::write_bytes(ptr, 1, len);
f(slice::from_raw_parts_mut(ptr.cast::<u8>(), len))
})
}
}
pub unsafe fn try_with_uninit(
len: usize,
f: impl FnOnce(&mut [MaybeUninit<u8>]),
) -> Result<Self, AllocError> {
let len_with_nul = len.checked_add(1).ok_or(AllocError(len))?;
let layout = Layout::array::<u8>(len_with_nul).map_err(|_| AllocError(len))?;
let Some(ptr) = A::alloc(layout) else {
return Err(AllocError(len_with_nul));
};
let Some((last, initme)) = unsafe {
slice::from_raw_parts_mut(ptr.cast::<MaybeUninit<u8>>().as_ptr(), len_with_nul)
}
.split_last_mut() else {
unreachable!("already incremented length, so slice is non-empty")
};
last.write(0);
f(initme);
Ok(Self {
ptr,
alloc: PhantomData,
})
}
}
impl<A: Allocator> Drop for SeaStringIn<A> {
fn drop(&mut self) {
unsafe { A::free(self.ptr) }
}
}
impl<A: Allocator> ops::Deref for SeaStringIn<A> {
type Target = SeaStr;
fn deref(&self) -> &Self::Target {
unsafe { self.ptr.cast::<SeaStr>().as_ref() }
}
}
impl<A: Allocator> ops::DerefMut for SeaStringIn<A> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.ptr.cast::<SeaStr>().as_mut() }
}
}
impl<A: Allocator> fmt::Debug for SeaStringIn<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
SeaStr::fmt(self, f)
}
}
impl<A: Allocator> fmt::Display for SeaStringIn<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
SeaStr::fmt(self, f)
}
}
impl<A: Allocator> fmt::Pointer for SeaStringIn<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
SeaStr::fmt(self, f)
}
}
impl<A1: Allocator, A2: Allocator> PartialEq<SeaStringIn<A2>> for SeaStringIn<A1> {
fn eq(&self, other: &SeaStringIn<A2>) -> bool {
self.as_cstr() == other.as_cstr()
}
}
impl<A: Allocator> Eq for SeaStringIn<A> {}
impl<A: Allocator> Hash for SeaStringIn<A> {
fn hash<H: Hasher>(&self, state: &mut H) {
SeaStr::hash(self, state)
}
}
impl<A: Allocator> Ord for SeaStringIn<A> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
SeaStr::cmp(self, other)
}
}
impl<A1: Allocator, A2: Allocator> PartialOrd<SeaStringIn<A2>> for SeaStringIn<A1> {
fn partial_cmp(&self, other: &SeaStringIn<A2>) -> Option<cmp::Ordering> {
SeaStr::partial_cmp(self, other)
}
}
impl<A: Allocator> AsRef<SeaStr> for SeaStringIn<A> {
fn as_ref(&self) -> &SeaStr {
self
}
}
impl<A: Allocator> Borrow<SeaStr> for SeaStringIn<A> {
fn borrow(&self) -> &SeaStr {
self
}
}
impl<A: Allocator> AsMut<SeaStr> for SeaStringIn<A> {
fn as_mut(&mut self) -> &mut SeaStr {
self
}
}
impl<A: Allocator> BorrowMut<SeaStr> for SeaStringIn<A> {
fn borrow_mut(&mut self) -> &mut SeaStr {
self
}
}
impl<A: Allocator> AsRef<[u8]> for SeaStringIn<A> {
fn as_ref(&self) -> &[u8] {
SeaStr::as_ref(self)
}
}
impl<A: Allocator> Borrow<[u8]> for SeaStringIn<A> {
fn borrow(&self) -> &[u8] {
SeaStr::borrow(self)
}
}
impl<A: Allocator> AsMut<[u8]> for SeaStringIn<A> {
fn as_mut(&mut self) -> &mut [u8] {
SeaStr::as_mut(self)
}
}
impl<A: Allocator> BorrowMut<[u8]> for SeaStringIn<A> {
fn borrow_mut(&mut self) -> &mut [u8] {
SeaStr::borrow_mut(self)
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
impl<A: Allocator> From<&SeaStr> for SeaStringIn<A> {
fn from(value: &SeaStr) -> Self {
Self::new(value.as_cstr())
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
impl<A: Allocator> From<&core::ffi::CStr> for SeaStringIn<A> {
fn from(value: &core::ffi::CStr) -> Self {
Self::new(value)
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
impl<A: Allocator> From<alloc::ffi::CString> for SeaStringIn<A> {
fn from(value: alloc::ffi::CString) -> Self {
Self::new(&value)
}
}