use core::{
borrow::{Borrow, BorrowMut},
cmp,
ffi::{c_char, c_void, CStr},
fmt::{self, Write as _},
hash::{Hash, Hasher},
ops, slice,
};
#[repr(transparent)]
pub struct SeaStr(
c_void,
);
impl SeaStr {
pub const unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a Self {
&*(ptr as *const SeaStr)
}
pub const fn as_ptr(&self) -> *const c_char {
self as *const Self as _
}
pub unsafe fn from_ptr_mut<'a>(ptr: *mut c_char) -> &'a mut Self {
&mut *(ptr as *mut SeaStr)
}
pub fn as_ptr_mut(&mut self) -> *mut c_char {
self as *mut Self as _
}
pub const fn is_empty(&self) -> bool {
unsafe { *self.as_ptr() == 0 }
}
pub const fn len(&self) -> usize {
self.as_cstr().to_bytes().len() }
pub const fn len_with_nul(&self) -> usize {
unsafe { self.len().unchecked_add(1) }
}
pub const fn bytes(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.as_ptr().cast::<u8>(), self.len()) }
}
pub const fn bytes_with_nul(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.as_ptr().cast::<u8>(), self.len_with_nul()) }
}
pub const fn as_cstr(&self) -> &CStr {
unsafe { CStr::from_ptr(self.as_ptr()) }
}
pub const fn from_cstr(c: &CStr) -> &Self {
unsafe { Self::from_ptr(c.as_ptr()) }
}
pub fn bytes_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.as_ptr_mut().cast::<u8>(), self.len()) }
}
pub unsafe fn bytes_with_nul_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.as_ptr_mut().cast::<u8>(), self.len_with_nul()) }
}
pub fn from_bytes_mut(src: &mut [u8]) -> &mut Self {
let Some(terminator) = src.last_mut() else {
panic!("cannot add terminator to empty slice")
};
*terminator = 0;
unsafe { Self::from_ptr_mut(src.as_mut_ptr().cast::<c_char>()) }
}
pub fn trunate(&mut self, len: usize) {
if let Some(dst) = self.get_mut(len + 1) {
*dst = 0
}
}
}
impl fmt::Debug for SeaStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("\"")?;
for chunk in self.bytes().utf8_chunks() {
f.write_fmt(format_args!("{}", chunk.valid().escape_default()))?;
if !chunk.invalid().is_empty() {
f.write_char(char::REPLACEMENT_CHARACTER)?
}
}
f.write_str("\"")?;
Ok(())
}
}
impl fmt::Display for SeaStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for chunk in self.bytes().utf8_chunks() {
f.write_str(chunk.valid())?;
if !chunk.invalid().is_empty() {
f.write_char(char::REPLACEMENT_CHARACTER)?
}
}
Ok(())
}
}
impl PartialEq for SeaStr {
fn eq(&self, other: &Self) -> bool {
self.bytes() == other.bytes()
}
}
impl PartialEq<CStr> for SeaStr {
fn eq(&self, other: &CStr) -> bool {
self.as_cstr() == other
}
}
impl Eq for SeaStr {}
impl Hash for SeaStr {
fn hash<H: Hasher>(&self, state: &mut H) {
self.bytes().hash(state);
}
}
impl Ord for SeaStr {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.bytes().cmp(other.bytes())
}
}
impl PartialOrd for SeaStr {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl AsRef<[u8]> for SeaStr {
fn as_ref(&self) -> &[u8] {
self.bytes()
}
}
impl Borrow<[u8]> for SeaStr {
fn borrow(&self) -> &[u8] {
self.bytes()
}
}
impl AsMut<[u8]> for SeaStr {
fn as_mut(&mut self) -> &mut [u8] {
self.bytes_mut()
}
}
impl BorrowMut<[u8]> for SeaStr {
fn borrow_mut(&mut self) -> &mut [u8] {
self.bytes_mut()
}
}
impl ops::Deref for SeaStr {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.bytes()
}
}
impl ops::DerefMut for SeaStr {
fn deref_mut(&mut self) -> &mut Self::Target {
self.bytes_mut()
}
}
impl<'a> From<&'a SeaStr> for &'a CStr {
fn from(value: &'a SeaStr) -> Self {
value.as_cstr()
}
}
impl<'a> From<&'a CStr> for &'a SeaStr {
fn from(value: &'a CStr) -> Self {
SeaStr::from_cstr(value)
}
}
impl Default for &'_ SeaStr {
fn default() -> Self {
SeaStr::from_cstr(c"")
}
}