use super::chars::{Char16, Char8, NUL_16, NUL_8};
use core::convert::TryInto;
use core::fmt;
use core::iter::Iterator;
use core::result::Result;
use core::slice;
pub enum FromSliceWithNulError {
InvalidChar(usize),
InteriorNul(usize),
NotNulTerminated,
}
#[repr(transparent)]
pub struct CStr8([Char8]);
impl CStr8 {
pub unsafe fn from_ptr<'ptr>(ptr: *const Char8) -> &'ptr Self {
let mut len = 0;
while *ptr.add(len) != NUL_8 {
len += 1
}
let ptr = ptr as *const u8;
Self::from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr, len + 1))
}
pub fn from_bytes_with_nul(chars: &[u8]) -> Result<&Self, FromSliceWithNulError> {
let nul_pos = chars.iter().position(|&c| c == 0);
if let Some(nul_pos) = nul_pos {
if nul_pos + 1 != chars.len() {
return Err(FromSliceWithNulError::InteriorNul(nul_pos));
}
Ok(unsafe { Self::from_bytes_with_nul_unchecked(chars) })
} else {
Err(FromSliceWithNulError::NotNulTerminated)
}
}
pub unsafe fn from_bytes_with_nul_unchecked(chars: &[u8]) -> &Self {
&*(chars as *const [u8] as *const Self)
}
pub fn as_ptr(&self) -> *const Char8 {
self.0.as_ptr()
}
pub fn to_bytes(&self) -> &[u8] {
let chars = self.to_bytes_with_nul();
&chars[..chars.len() - 1]
}
pub fn to_bytes_with_nul(&self) -> &[u8] {
unsafe { &*(&self.0 as *const [Char8] as *const [u8]) }
}
}
#[repr(transparent)]
pub struct CStr16([Char16]);
impl CStr16 {
pub unsafe fn from_ptr<'ptr>(ptr: *const Char16) -> &'ptr Self {
let mut len = 0;
while *ptr.add(len) != NUL_16 {
len += 1
}
let ptr = ptr as *const u16;
Self::from_u16_with_nul_unchecked(slice::from_raw_parts(ptr, len + 1))
}
pub fn from_u16_with_nul(codes: &[u16]) -> Result<&Self, FromSliceWithNulError> {
for (pos, &code) in codes.iter().enumerate() {
match code.try_into() {
Ok(NUL_16) => {
if pos != codes.len() - 1 {
return Err(FromSliceWithNulError::InteriorNul(pos));
} else {
return Ok(unsafe { Self::from_u16_with_nul_unchecked(codes) });
}
}
Err(_) => {
return Err(FromSliceWithNulError::InvalidChar(pos));
}
_ => {}
}
}
Err(FromSliceWithNulError::NotNulTerminated)
}
pub unsafe fn from_u16_with_nul_unchecked(codes: &[u16]) -> &Self {
&*(codes as *const [u16] as *const Self)
}
pub fn as_ptr(&self) -> *const Char16 {
self.0.as_ptr()
}
pub fn to_u16_slice(&self) -> &[u16] {
let chars = self.to_u16_slice_with_nul();
&chars[..chars.len() - 1]
}
pub fn to_u16_slice_with_nul(&self) -> &[u16] {
unsafe { &*(&self.0 as *const [Char16] as *const [u16]) }
}
pub fn iter(&self) -> CStr16Iter {
CStr16Iter {
inner: self,
pos: 0,
}
}
}
#[derive(Debug)]
pub struct CStr16Iter<'a> {
inner: &'a CStr16,
pos: usize,
}
impl<'a> Iterator for CStr16Iter<'a> {
type Item = &'a Char16;
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.inner.0.len() - 1 {
None
} else {
self.pos += 1;
self.inner.0.get(self.pos - 1)
}
}
}
impl fmt::Debug for CStr16 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "CStr16({:?})", &self.0)
}
}
impl fmt::Display for CStr16 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for c in self.iter() {
<Char16 as fmt::Display>::fmt(&c, f)?;
}
Ok(())
}
}