use uefi_raw::Status;
use super::UnalignedSlice;
use super::chars::{Char8, Char16, NUL_8, NUL_16};
use crate::mem::PoolAllocation;
use crate::polyfill::maybe_uninit_slice_assume_init_ref;
use core::borrow::Borrow;
use core::ffi::CStr;
use core::fmt::{self, Display, Formatter};
use core::mem::MaybeUninit;
use core::ops::Deref;
use core::ptr::NonNull;
use core::{ptr, slice};
#[cfg(feature = "alloc")]
use super::CString16;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FromSliceUntilNulError {
InvalidChar(usize),
NoNul,
}
impl Display for FromSliceUntilNulError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidChar(usize) => write!(f, "invalid character at index {usize}"),
Self::NoNul => write!(f, "no nul character"),
}
}
}
impl core::error::Error for FromSliceUntilNulError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FromSliceWithNulError {
InvalidChar(usize),
InteriorNul(usize),
NotNulTerminated,
NotAligned,
}
impl Display for FromSliceWithNulError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidChar(usize) => write!(f, "invalid character at index {usize}"),
Self::InteriorNul(usize) => write!(f, "interior null character at index {usize}"),
Self::NotNulTerminated => write!(f, "not null-terminated"),
Self::NotAligned => write!(f, "slice is not aligned to a 16-bit boundary"),
}
}
}
impl core::error::Error for FromSliceWithNulError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum UnalignedCStr16Error {
InvalidChar(usize),
InteriorNul(usize),
NotNulTerminated,
BufferTooSmall,
}
impl Display for UnalignedCStr16Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidChar(usize) => write!(f, "invalid character at index {usize}"),
Self::InteriorNul(usize) => write!(f, "interior null character at index {usize}"),
Self::NotNulTerminated => write!(f, "not null-terminated"),
Self::BufferTooSmall => write!(f, "buffer too small"),
}
}
}
impl core::error::Error for UnalignedCStr16Error {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FromStrWithBufError {
InvalidChar(usize),
InteriorNul(usize),
BufferTooSmall,
}
impl Display for FromStrWithBufError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidChar(usize) => write!(f, "invalid character at index {usize}"),
Self::InteriorNul(usize) => write!(f, "interior null character at index {usize}"),
Self::BufferTooSmall => write!(f, "buffer too small"),
}
}
}
impl core::error::Error for FromStrWithBufError {}
#[repr(transparent)]
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct CStr8([Char8]);
impl CStr8 {
#[must_use]
pub unsafe fn from_ptr<'ptr>(ptr: *const Char8) -> &'ptr Self {
let mut len = 0;
while unsafe { *ptr.add(len) } != NUL_8 {
len += 1
}
let ptr = ptr.cast::<u8>();
unsafe { 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)
}
}
#[must_use]
pub const unsafe fn from_bytes_with_nul_unchecked(chars: &[u8]) -> &Self {
unsafe { &*(ptr::from_ref(chars) as *const Self) }
}
#[must_use]
pub const fn as_ptr(&self) -> *const Char8 {
self.0.as_ptr()
}
#[must_use]
pub const fn as_bytes(&self) -> &[u8] {
unsafe { &*(ptr::from_ref(&self.0) as *const [u8]) }
}
}
impl fmt::Debug for CStr8 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "CStr8({:?})", &self.0)
}
}
impl fmt::Display for CStr8 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for c in &self.0[..&self.0.len() - 1] {
<Char8 as fmt::Display>::fmt(c, f)?;
}
Ok(())
}
}
impl AsRef<[u8]> for CStr8 {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl Borrow<[u8]> for CStr8 {
fn borrow(&self) -> &[u8] {
self.as_bytes()
}
}
impl<StrType: AsRef<str> + ?Sized> EqStrUntilNul<StrType> for CStr8 {
fn eq_str_until_nul(&self, other: &StrType) -> bool {
let other = other.as_ref();
let any_not_equal = self
.0
.iter()
.copied()
.map(char::from)
.zip(other.chars())
.take_while(|(l, r)| *l != '\0' && *r != '\0')
.any(|(l, r)| l != r);
!any_not_equal
}
}
impl<'a> TryFrom<&'a CStr> for &'a CStr8 {
type Error = FromSliceWithNulError;
fn try_from(cstr: &'a CStr) -> Result<Self, Self::Error> {
CStr8::from_bytes_with_nul(cstr.to_bytes_with_nul())
}
}
const unsafe fn latin1_from_utf8_at_offset(bytes: &[u8], offset: usize) -> (u8, usize) {
if bytes[offset] & 0b1000_0000 == 0b0000_0000 {
(bytes[offset], 1)
} else if bytes[offset] & 0b1110_0000 == 0b1100_0000 {
let a = (bytes[offset] & 0b0001_1111) as u16;
let b = (bytes[offset + 1] & 0b0011_1111) as u16;
let ch = (a << 6) | b;
if ch > 0xff {
panic!("input string cannot be encoded as Latin-1");
}
(ch as u8, 2)
} else {
panic!("input string cannot be encoded as Latin-1");
}
}
#[must_use]
pub const fn str_num_latin1_chars(s: &str) -> usize {
let bytes = s.as_bytes();
let len = bytes.len();
let mut offset = 0;
let mut num_latin1_chars = 0;
while offset < len {
let (_, num_utf8_bytes) = unsafe { latin1_from_utf8_at_offset(bytes, offset) };
offset += num_utf8_bytes;
num_latin1_chars += 1;
}
num_latin1_chars
}
#[must_use]
pub const fn str_to_latin1<const N: usize>(s: &str) -> [u8; N] {
let bytes = s.as_bytes();
let len = bytes.len();
let mut output = [0; N];
let mut output_offset = 0;
let mut input_offset = 0;
while input_offset < len {
let (ch, num_utf8_bytes) = unsafe { latin1_from_utf8_at_offset(bytes, input_offset) };
if ch == 0 {
panic!("interior null character");
} else {
output[output_offset] = ch;
output_offset += 1;
input_offset += num_utf8_bytes;
}
}
if output_offset + 1 != N {
panic!("incorrect array length");
}
output
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct CStr16([Char16]);
impl CStr16 {
#[must_use]
pub unsafe fn from_ptr<'ptr>(ptr: *const Char16) -> &'ptr Self {
let mut len = 0;
while unsafe { *ptr.add(len) } != NUL_16 {
len += 1
}
let ptr = ptr.cast::<u16>();
unsafe { Self::from_u16_with_nul_unchecked(slice::from_raw_parts(ptr, len + 1)) }
}
pub fn from_u16_until_nul(codes: &[u16]) -> Result<&Self, FromSliceUntilNulError> {
for (pos, &code) in codes.iter().enumerate() {
let chr =
Char16::try_from(code).map_err(|_| FromSliceUntilNulError::InvalidChar(pos))?;
if chr == NUL_16 {
return Ok(unsafe { Self::from_u16_with_nul_unchecked(&codes[..=pos]) });
}
}
Err(FromSliceUntilNulError::NoNul)
}
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)
}
#[must_use]
pub const unsafe fn from_u16_with_nul_unchecked(codes: &[u16]) -> &Self {
unsafe { &*(ptr::from_ref(codes) as *const Self) }
}
pub fn from_char16_until_nul(chars: &[Char16]) -> Result<&Self, FromSliceUntilNulError> {
let end = chars
.iter()
.position(|c| *c == NUL_16)
.ok_or(FromSliceUntilNulError::NoNul)?;
unsafe { Ok(Self::from_char16_with_nul_unchecked(&chars[..=end])) }
}
pub fn from_char16_with_nul(chars: &[Char16]) -> Result<&Self, FromSliceWithNulError> {
if chars.is_empty() {
return Err(FromSliceWithNulError::NotNulTerminated);
}
if let Some(null_index) = chars.iter().position(|c| *c == NUL_16) {
if null_index == chars.len() - 1 {
Ok(unsafe { Self::from_char16_with_nul_unchecked(chars) })
} else {
Err(FromSliceWithNulError::InteriorNul(null_index))
}
} else {
Err(FromSliceWithNulError::NotNulTerminated)
}
}
#[must_use]
pub const unsafe fn from_char16_with_nul_unchecked(chars: &[Char16]) -> &Self {
let ptr: *const [Char16] = chars;
unsafe { &*(ptr as *const Self) }
}
pub fn from_str_with_buf<'a>(
input: &str,
buf: &'a mut [u16],
) -> Result<&'a Self, FromStrWithBufError> {
let mut index = 0;
for c in input.encode_utf16() {
*buf.get_mut(index)
.ok_or(FromStrWithBufError::BufferTooSmall)? = c;
index += 1;
}
*buf.get_mut(index)
.ok_or(FromStrWithBufError::BufferTooSmall)? = 0;
Self::from_u16_with_nul(&buf[..index + 1]).map_err(|err| match err {
FromSliceWithNulError::InvalidChar(p) => FromStrWithBufError::InvalidChar(p),
FromSliceWithNulError::InteriorNul(p) => FromStrWithBufError::InteriorNul(p),
FromSliceWithNulError::NotNulTerminated | FromSliceWithNulError::NotAligned => {
unreachable!()
}
})
}
pub fn from_unaligned_slice<'buf>(
src: &UnalignedSlice<'_, u16>,
buf: &'buf mut [MaybeUninit<u16>],
) -> Result<&'buf Self, UnalignedCStr16Error> {
let buf = buf
.get_mut(..src.len())
.ok_or(UnalignedCStr16Error::BufferTooSmall)?;
src.copy_to_maybe_uninit(buf);
let buf = unsafe {
maybe_uninit_slice_assume_init_ref(buf)
};
Self::from_u16_with_nul(buf).map_err(|e| match e {
FromSliceWithNulError::InvalidChar(v) => UnalignedCStr16Error::InvalidChar(v),
FromSliceWithNulError::InteriorNul(v) => UnalignedCStr16Error::InteriorNul(v),
FromSliceWithNulError::NotNulTerminated => UnalignedCStr16Error::NotNulTerminated,
FromSliceWithNulError::NotAligned => unreachable!(),
})
}
pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromSliceWithNulError> {
if !bytes.len().is_multiple_of(size_of::<Char16>()) || !bytes.ends_with(&[0, 0]) {
return Err(FromSliceWithNulError::NotNulTerminated);
}
if bytes.as_ptr().align_offset(2) != 0 {
return Err(FromSliceWithNulError::NotAligned);
}
let u16_slice =
unsafe { slice::from_raw_parts(bytes.as_ptr().cast::<u16>(), bytes.len() / 2) };
Self::from_u16_with_nul(u16_slice)
}
#[must_use]
pub const fn as_ptr(&self) -> *const Char16 {
self.0.as_ptr()
}
#[must_use]
pub fn as_slice(&self) -> &[Char16] {
&self.0[..self.num_chars()]
}
#[must_use]
pub const fn as_slice_with_nul(&self) -> &[Char16] {
&self.0
}
#[must_use]
pub fn to_u16_slice(&self) -> &[u16] {
let chars = self.to_u16_slice_with_nul();
&chars[..chars.len() - 1]
}
#[must_use]
pub const fn to_u16_slice_with_nul(&self) -> &[u16] {
unsafe { &*(ptr::from_ref(&self.0) as *const [u16]) }
}
#[must_use]
pub const fn iter(&self) -> CStr16Iter<'_> {
CStr16Iter {
inner: self,
pos: 0,
}
}
#[must_use]
pub const fn num_chars(&self) -> usize {
self.0.len() - 1
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.num_chars() == 0
}
#[must_use]
pub const fn num_bytes(&self) -> usize {
self.0.len() * 2
}
#[must_use]
pub fn is_ascii(&self) -> bool {
self.0.iter().all(|c| c.is_ascii())
}
pub fn as_str_in_buf(&self, buf: &mut dyn core::fmt::Write) -> core::fmt::Result {
for c16 in self.iter() {
buf.write_char(char::from(*c16))?;
}
Ok(())
}
#[must_use]
pub const fn as_bytes(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.0.as_ptr().cast(), self.num_bytes()) }
}
}
impl AsRef<[u8]> for CStr16 {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl Borrow<[u8]> for CStr16 {
fn borrow(&self) -> &[u8] {
self.as_bytes()
}
}
#[cfg(feature = "alloc")]
impl From<&CStr16> for alloc::string::String {
fn from(value: &CStr16) -> Self {
value
.as_slice()
.iter()
.copied()
.map(u16::from)
.map(u32::from)
.map(|int| char::from_u32(int).expect("Should be encodable as UTF-8"))
.collect::<Self>()
}
}
impl<StrType: AsRef<str> + ?Sized> EqStrUntilNul<StrType> for CStr16 {
fn eq_str_until_nul(&self, other: &StrType) -> bool {
let other = other.as_ref();
let any_not_equal = self
.iter()
.copied()
.map(char::from)
.zip(other.chars())
.take_while(|(l, r)| *l != '\0' && *r != '\0')
.any(|(l, r)| l != r);
!any_not_equal
}
}
impl AsRef<Self> for CStr16 {
fn as_ref(&self) -> &Self {
self
}
}
#[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(())
}
}
#[cfg(feature = "alloc")]
impl PartialEq<CString16> for &CStr16 {
fn eq(&self, other: &CString16) -> bool {
PartialEq::eq(*self, other.as_ref())
}
}
#[derive(Debug)]
pub struct PoolString(PoolAllocation);
impl PoolString {
pub unsafe fn new(text: *const Char16) -> crate::Result<Self> {
NonNull::new(text.cast_mut())
.map(|p| Self(PoolAllocation::new(p.cast())))
.ok_or_else(|| Status::OUT_OF_RESOURCES.into())
}
}
impl Deref for PoolString {
type Target = CStr16;
fn deref(&self) -> &Self::Target {
unsafe { CStr16::from_ptr(self.0.as_ptr().as_ptr().cast()) }
}
}
impl UnalignedSlice<'_, u16> {
pub fn to_cstr16<'buf>(
&self,
buf: &'buf mut [MaybeUninit<u16>],
) -> Result<&'buf CStr16, UnalignedCStr16Error> {
CStr16::from_unaligned_slice(self, buf)
}
}
pub trait EqStrUntilNul<StrType: ?Sized> {
fn eq_str_until_nul(&self, other: &StrType) -> bool;
}
impl<StrType, C16StrType> EqStrUntilNul<C16StrType> for StrType
where
StrType: AsRef<str>,
C16StrType: EqStrUntilNul<StrType> + ?Sized,
{
fn eq_str_until_nul(&self, other: &C16StrType) -> bool {
other.eq_str_until_nul(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{cstr8, cstr16};
use alloc::format;
use alloc::string::String;
#[test]
fn test_cstr8_from_cstr() {
let msg = "hello world\0";
let cstr = unsafe { CStr::from_ptr(msg.as_ptr().cast()) };
let cstr8: &CStr8 = TryFrom::try_from(cstr).unwrap();
assert!(cstr8.eq_str_until_nul(msg));
assert!(msg.eq_str_until_nul(cstr8));
}
#[test]
fn test_cstr8_as_bytes() {
let string: &CStr8 = cstr8!("a");
assert_eq!(string.as_bytes(), &[b'a', 0]);
assert_eq!(<CStr8 as AsRef<[u8]>>::as_ref(string), &[b'a', 0]);
assert_eq!(<CStr8 as Borrow<[u8]>>::borrow(string), &[b'a', 0]);
}
#[test]
fn test_cstr8_display() {
let s = cstr8!("abc");
assert_eq!(format!("{s}"), "abc");
}
#[test]
fn test_cstr16_display() {
let s = cstr16!("abc");
assert_eq!(format!("{s}"), "abc");
}
#[test]
fn test_cstr16_num_bytes() {
let s = CStr16::from_u16_with_nul(&[65, 66, 67, 0]).unwrap();
assert_eq!(s.num_bytes(), 8);
}
#[test]
fn test_cstr16_from_u16_until_nul() {
assert_eq!(
CStr16::from_u16_until_nul(&[]),
Err(FromSliceUntilNulError::NoNul)
);
assert_eq!(
CStr16::from_u16_until_nul(&[65, 66]),
Err(FromSliceUntilNulError::NoNul)
);
assert_eq!(
CStr16::from_u16_until_nul(&[65, 0xde01, 0]),
Err(FromSliceUntilNulError::InvalidChar(1))
);
assert_eq!(CStr16::from_u16_until_nul(&[97, 98, 0,]), Ok(cstr16!("ab")));
assert_eq!(
CStr16::from_u16_until_nul(&[97, 0, 98, 0,]),
Ok(cstr16!("a"))
);
}
#[test]
fn test_cstr16_from_char16_until_nul() {
assert_eq!(
CStr16::from_char16_until_nul(&[]),
Err(FromSliceUntilNulError::NoNul)
);
assert_eq!(
CStr16::from_char16_until_nul(&[
Char16::try_from('a').unwrap(),
Char16::try_from('b').unwrap(),
]),
Err(FromSliceUntilNulError::NoNul)
);
assert_eq!(
CStr16::from_char16_until_nul(&[
Char16::try_from('a').unwrap(),
Char16::try_from('b').unwrap(),
NUL_16,
]),
Ok(cstr16!("ab"))
);
assert_eq!(
CStr16::from_char16_until_nul(&[
Char16::try_from('a').unwrap(),
NUL_16,
Char16::try_from('b').unwrap(),
NUL_16
]),
Ok(cstr16!("a"))
);
}
#[test]
fn test_cstr16_from_char16_with_nul() {
assert_eq!(
CStr16::from_char16_with_nul(&[]),
Err(FromSliceWithNulError::NotNulTerminated)
);
assert_eq!(
CStr16::from_char16_with_nul(&[
Char16::try_from('a').unwrap(),
NUL_16,
Char16::try_from('b').unwrap(),
NUL_16
]),
Err(FromSliceWithNulError::InteriorNul(1))
);
assert_eq!(
CStr16::from_char16_with_nul(&[
Char16::try_from('a').unwrap(),
Char16::try_from('b').unwrap(),
]),
Err(FromSliceWithNulError::NotNulTerminated)
);
assert_eq!(
CStr16::from_char16_with_nul(&[
Char16::try_from('a').unwrap(),
Char16::try_from('b').unwrap(),
NUL_16,
]),
Ok(cstr16!("ab"))
);
}
#[test]
fn test_cstr16_from_str_with_buf() {
let mut buf = [0; 4];
let s = CStr16::from_str_with_buf("ABC", &mut buf).unwrap();
assert_eq!(s.to_u16_slice_with_nul(), [65, 66, 67, 0]);
let s = CStr16::from_str_with_buf("A", &mut buf).unwrap();
assert_eq!(s.to_u16_slice_with_nul(), [65, 0]);
assert_eq!(
CStr16::from_str_with_buf("ABCD", &mut buf).unwrap_err(),
FromStrWithBufError::BufferTooSmall
);
assert_eq!(
CStr16::from_str_with_buf("a😀", &mut buf).unwrap_err(),
FromStrWithBufError::InvalidChar(1),
);
assert_eq!(
CStr16::from_str_with_buf("a\0b", &mut buf).unwrap_err(),
FromStrWithBufError::InteriorNul(1),
);
}
#[test]
fn test_cstr16_macro() {
assert_eq!(
crate::prelude::cstr16!("ABC").to_u16_slice_with_nul(),
[65, 66, 67, 0]
)
}
#[test]
fn test_unaligned_cstr16() {
let mut buf = [0u16; 6];
let us = unsafe {
let ptr = buf.as_mut_ptr().cast::<u8>();
let ptr = ptr.add(1).cast::<u16>();
ptr.add(0).write_unaligned(b't'.into());
ptr.add(1).write_unaligned(b'e'.into());
ptr.add(2).write_unaligned(b's'.into());
ptr.add(3).write_unaligned(b't'.into());
ptr.add(4).write_unaligned(b'\0'.into());
UnalignedSlice::new(ptr, 5)
};
let mut buf = [MaybeUninit::new(0); 4];
assert_eq!(
us.to_cstr16(&mut buf).unwrap_err(),
UnalignedCStr16Error::BufferTooSmall
);
let mut buf = [MaybeUninit::new(0); 5];
assert_eq!(
us.to_cstr16(&mut buf).unwrap(),
CString16::try_from("test").unwrap()
);
assert_eq!(
us.to_cstring16().unwrap(),
CString16::try_from("test").unwrap()
);
}
#[test]
fn test_cstr16_as_slice() {
let string: &CStr16 = cstr16!("a");
assert_eq!(string.as_slice(), &[Char16::try_from('a').unwrap()]);
assert_eq!(
string.as_slice_with_nul(),
&[Char16::try_from('a').unwrap(), NUL_16]
);
}
#[test]
fn test_cstr16_as_bytes() {
let string: &CStr16 = cstr16!("a");
assert_eq!(string.as_bytes(), &[b'a', 0, 0, 0]);
assert_eq!(<CStr16 as AsRef<[u8]>>::as_ref(string), &[b'a', 0, 0, 0]);
assert_eq!(<CStr16 as Borrow<[u8]>>::borrow(string), &[b'a', 0, 0, 0]);
}
#[allow(non_snake_case)]
macro_rules! test_compare_cstrX {
($input:ident) => {
assert!($input.eq_str_until_nul(&"test"));
assert!($input.eq_str_until_nul(&String::from("test")));
assert!(String::from("test").eq_str_until_nul($input));
assert!("test".eq_str_until_nul($input));
assert!($input.eq_str_until_nul(&"te\0st"));
assert!($input.eq_str_until_nul(&"test\0"));
assert!(!$input.eq_str_until_nul(&"hello"));
};
}
#[test]
fn test_compare_cstr8() {
let input: &CStr8 = cstr8!("test");
test_compare_cstrX!(input);
}
#[test]
fn test_compare_cstr16() {
let input: &CStr16 = cstr16!("test");
test_compare_cstrX!(input);
}
#[test]
fn test_cstr16_macro_const() {
const S: &CStr16 = cstr16!("ABC");
assert_eq!(S.to_u16_slice_with_nul(), [65, 66, 67, 0]);
}
#[test]
fn test_cstr8_eq_std_str() {
let input: &CStr8 = cstr8!("test");
assert!(input.eq_str_until_nul("test")); assert!(input.eq_str_until_nul(&"test"));
assert!(input.eq_str_until_nul(&String::from("test")));
assert!(String::from("test").eq_str_until_nul(input));
assert!("test".eq_str_until_nul(input));
}
#[test]
fn test_cstr16_eq_std_str() {
let input: &CStr16 = cstr16!("test");
assert!(input.eq_str_until_nul("test")); assert!(input.eq_str_until_nul(&"test"));
assert!(input.eq_str_until_nul(&String::from("test")));
assert!(String::from("test").eq_str_until_nul(input));
assert!("test".eq_str_until_nul(input));
}
#[test]
fn test_cstr16_from_bytes_with_nul() {
let aligned: &[u16] = &[b'A' as u16, b'B' as u16, 0];
let bytes = unsafe { slice::from_raw_parts(aligned.as_ptr().cast::<u8>(), 6) };
let s = CStr16::from_bytes_with_nul(bytes).unwrap();
assert_eq!(s.to_u16_slice_with_nul(), &[65, 66, 0]);
let aligned: &[u16] = &[b'A' as u16, 0];
let bytes = unsafe { slice::from_raw_parts(aligned.as_ptr().cast::<u8>(), 3) };
assert_eq!(
CStr16::from_bytes_with_nul(bytes),
Err(FromSliceWithNulError::NotNulTerminated)
);
let aligned: &[u16] = &[b'A' as u16, b'B' as u16];
let bytes = unsafe { slice::from_raw_parts(aligned.as_ptr().cast::<u8>(), 4) };
assert_eq!(
CStr16::from_bytes_with_nul(bytes),
Err(FromSliceWithNulError::NotNulTerminated)
);
let aligned: &[u16] = &[b'A' as u16, 0x0100];
let bytes = unsafe { slice::from_raw_parts(aligned.as_ptr().cast::<u8>(), 4) };
assert_eq!(
CStr16::from_bytes_with_nul(bytes),
Err(FromSliceWithNulError::NotNulTerminated)
);
let aligned: &[u16] = &[b'A' as u16, 0, b'B' as u16, 0];
let bytes = unsafe { slice::from_raw_parts(aligned.as_ptr().cast::<u8>(), 8) };
assert_eq!(
CStr16::from_bytes_with_nul(bytes),
Err(FromSliceWithNulError::InteriorNul(1))
);
let aligned: &[u16] = &[0xd800, 0];
let bytes = unsafe { slice::from_raw_parts(aligned.as_ptr().cast::<u8>(), 4) };
assert_eq!(
CStr16::from_bytes_with_nul(bytes),
Err(FromSliceWithNulError::InvalidChar(0))
);
let aligned: &[u16] = &[b'A' as u16, 0, 0];
let bytes = unsafe { slice::from_raw_parts(aligned.as_ptr().cast::<u8>().add(1), 4) };
assert_eq!(
CStr16::from_bytes_with_nul(bytes),
Err(FromSliceWithNulError::NotAligned)
);
let aligned: &[u16] = &[0];
let bytes = unsafe { slice::from_raw_parts(aligned.as_ptr().cast::<u8>(), 2) };
let s = CStr16::from_bytes_with_nul(bytes).unwrap();
assert!(s.is_empty());
}
}