use crate::*;
use std::borrow::Cow;
use std::fmt::{self, Debug, Formatter};
use std::ffi::*;
use std::str::*;
#[cfg(doc)] use std::os::raw::c_char;
#[repr(transparent)]
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CStrBuf<B> {
buffer: B,
}
impl<B: AsRef<[u8]> + AsMut<[u8]> + Default> CStrBuf<B> {
pub fn from_truncate(data: &(impl AsRef<[u8]> + ?Sized)) -> Self {
let mut s = Self::default();
let _ = s.set_truncate(data);
s
}
pub unsafe fn from_truncate_without_nul(data: &(impl AsRef<[u8]> + ?Sized)) -> Self {
let mut s = Self::default();
let _ = s.set_truncate_without_nul(data);
s
}
pub fn try_from(data: &(impl AsRef<[u8]> + ?Sized)) -> Result<Self, BufferTooSmallError> {
let mut s = Self::default();
s.try_set(data)?;
Ok(s)
}
pub unsafe fn try_from_without_nul(data: &(impl AsRef<[u8]> + ?Sized)) -> Result<Self, BufferTooSmallError> {
let mut s = Self::default();
s.try_set_without_nul(data)?;
Ok(s)
}
}
impl<B: AsRef<[u8]>> CStrBuf<B> {
pub fn buffer(&self) -> &[u8] { self.buffer.as_ref() }
pub fn is_empty(&self) -> bool { self.buffer.as_ref().first().copied() == Some(0) }
pub fn to_bytes(&self) -> &[u8] {
let buffer = self.buffer.as_ref();
match buffer.iter().copied().position(|ch| ch == 0) {
Some(nul) => &buffer[..nul],
None => buffer,
}
}
pub fn to_bytes_with_nul(&self) -> Result<&[u8], NotNulTerminatedError> {
let buffer = self.buffer.as_ref();
match buffer.iter().copied().position(|ch| ch == 0) {
Some(nul) => Ok(&buffer[..=nul]),
None => Err(NotNulTerminatedError(())),
}
}
pub fn to_cstr(&self) -> Result<&CStr, NotNulTerminatedError> { self.to_bytes_with_nul().map(|bytes| unsafe { CStr::from_bytes_with_nul_unchecked(bytes) }) }
pub fn to_str(&self) -> Result<&str, Utf8Error> { from_utf8(self.to_bytes()) }
pub fn to_string_lossy(&self) -> Cow<'_, str> { String::from_utf8_lossy(self.to_bytes()) }
pub fn validate(&self) -> Result<(), NotNulTerminatedError> { self.to_bytes_with_nul().map(|_| ()) }
}
impl<B: AsMut<[u8]>> CStrBuf<B> {
pub unsafe fn buffer_mut(&mut self) -> &mut [u8] { self.buffer.as_mut() }
pub fn nul_truncate(&mut self) -> CStrNonNull {
let buffer = self.buffer.as_mut();
*buffer.last_mut().unwrap() = 0;
unsafe { CStrNonNull::from_ptr_unchecked_unbounded(buffer.as_ptr().cast()) }
}
pub fn set_truncate(&mut self, data: &(impl AsRef<[u8]> + ?Sized)) -> Result<(), BufferTooSmallError> {
let src = data.as_ref();
let dst = self.buffer.as_mut();
let n = (dst.len()-1).min(src.len());
dst[..n].copy_from_slice(&src[..n]);
dst[n] = b'\0';
if src.len() >= dst.len() { Err(BufferTooSmallError(()))? }
Ok(())
}
pub unsafe fn set_truncate_without_nul(&mut self, data: &(impl AsRef<[u8]> + ?Sized)) -> Result<(), BufferTooSmallError> {
let src = data.as_ref();
let dst = self.buffer.as_mut();
let n = dst.len().min(src.len());
dst[..n].copy_from_slice(&src[..n]);
if let Some(dst) = dst.get_mut(n) { *dst = b'\0'; }
if src.len() > dst.len() { Err(BufferTooSmallError(()))? }
Ok(())
}
pub fn try_set(&mut self, data: &(impl AsRef<[u8]> + ?Sized)) -> Result<(), BufferTooSmallError> {
let src = data.as_ref();
let dst = self.buffer.as_mut();
if src.len() >= dst.len() { Err(BufferTooSmallError(()))? }
dst[..src.len()].copy_from_slice(src);
dst[src.len()] = b'\0';
Ok(())
}
pub unsafe fn try_set_without_nul(&mut self, data: &(impl AsRef<[u8]> + ?Sized)) -> Result<(), BufferTooSmallError> {
let src = data.as_ref();
let dst = self.buffer.as_mut();
if src.len() > dst.len() { Err(BufferTooSmallError(()))? }
dst[..src.len()].copy_from_slice(src);
if let Some(dst) = dst.get_mut(src.len()) { *dst = b'\0'; }
Ok(())
}
}
impl<B: AsRef<[u8]>> Debug for CStrBuf<B> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { crate::fmt::cstr_bytes(self.to_bytes(), f) }
}
#[cfg(feature = "bytemuck")] mod _bytemuck {
use super::*;
unsafe impl<B: bytemuck::Pod > bytemuck::Pod for CStrBuf<B> {}
unsafe impl<B: bytemuck::Zeroable > bytemuck::Zeroable for CStrBuf<B> {}
}
#[test] fn abi_layout() {
use std::os::raw::c_char;
assert_abi_compatible!([c_char; 1], CStrBuf<[u8; 1]>);
assert_abi_compatible!([c_char; 2], CStrBuf<[u8; 2]>);
assert_abi_compatible!([c_char; 3], CStrBuf<[u8; 3]>);
assert_abi_compatible!([c_char; 4], CStrBuf<[u8; 4]>);
assert_abi_compatible!([c_char; 6], CStrBuf<[u8; 6]>);
assert_abi_compatible!([c_char; 8], CStrBuf<[u8; 8]>);
assert_abi_compatible!([c_char; 12], CStrBuf<[u8; 12]>);
assert_abi_compatible!([c_char; 16], CStrBuf<[u8; 16]>);
assert_abi_compatible!([c_char; 24], CStrBuf<[u8; 24]>);
assert_abi_compatible!([c_char; 99], CStrBuf<[u8; 99]>);
}
#[test] fn from() {
type CB8 = CStrBuf<[u8; 8]>;
{
assert_eq!(CB8::from_truncate(b"1234567890").to_bytes(), b"1234567");
}
unsafe {
assert_eq!(CB8::from_truncate_without_nul(b"1234567890").to_bytes(), b"12345678");
}
{
assert_eq!(CB8::try_from(b"1234567890").is_err(), true);
assert_eq!(CB8::try_from(b"12345678" ).is_err(), true);
assert_eq!(CB8::try_from(b"1234567" ).unwrap().to_bytes(), b"1234567");
}
unsafe {
assert_eq!(CB8::try_from_without_nul(b"1234567890").is_err(), true);
assert_eq!(CB8::try_from_without_nul(b"12345678" ).unwrap().to_bytes(), b"12345678");
assert_eq!(CB8::try_from_without_nul(b"1234567" ).unwrap().to_bytes(), b"1234567");
}
}
#[test] fn set() {
type CB8 = CStrBuf<[u8; 8]>;
let reference = CB8::from_truncate(b"ref");
{
let mut cb = reference;
assert_eq!(cb.set_truncate(b"1234567890").is_err(), true);
assert_eq!(cb.to_bytes(), b"1234567");
assert_eq!(cb.set_truncate(b"1234").is_err(), false);
assert_eq!(cb.to_bytes(), b"1234");
}
unsafe {
let mut cb = reference;
assert_eq!(cb.set_truncate_without_nul(b"1234567890").is_err(), true);
assert_eq!(cb.to_bytes(), b"12345678");
assert_eq!(cb.set_truncate_without_nul(b"1234").is_err(), false);
assert_eq!(cb.to_bytes(), b"1234");
}
{
let mut cb = reference;
assert_eq!(cb.try_set(b"1234567890").is_err(), true);
assert_eq!(cb.to_bytes(), b"ref");
assert_eq!(cb.try_set(b"12345678").is_err(), true);
assert_eq!(cb.to_bytes(), b"ref");
assert_eq!(cb.try_set(b"1234").is_err(), false);
assert_eq!(cb.to_bytes(), b"1234");
}
unsafe {
let mut cb = reference;
assert_eq!(cb.try_set_without_nul(b"1234567890").is_err(), true);
assert_eq!(cb.to_bytes(), b"ref");
assert_eq!(cb.try_set_without_nul(b"12345678").is_err(), false);
assert_eq!(cb.to_bytes(), b"12345678");
assert_eq!(cb.try_set_without_nul(b"1234").is_err(), false);
assert_eq!(cb.to_bytes(), b"1234");
}
}
#[allow(overflowing_literals)]
#[test] fn struct_interop() {
use std::mem::*;
use std::os::raw::c_char;
#[repr(C)] struct C {
empty: [c_char; 16],
empty2: [c_char; 16],
empty3: [c_char; 16],
example: [c_char; 16],
full: [c_char; 16],
not_unicode: [c_char; 16],
}
let mut c = C {
empty: [0; 16],
empty2: [b'f' as _; 16],
empty3: [b'f' as _; 16],
example: [0; 16],
full: [b'f' as _; 16],
not_unicode: [0; 16],
};
c.empty2[0] = 0;
c.not_unicode[0] = 0xFF as c_char;
c.not_unicode[1] = 0xFF as c_char;
assert_abi_compatible!(R, C);
#[repr(C)] struct R {
empty: CStrBuf<[u8; 16]>,
empty2: CStrBuf<[u8; 16]>,
empty3: CStrBuf<[u8; 16]>,
example: CStrBuf<[u8; 16]>,
full: CStrBuf<[u8; 16]>,
not_unicode: CStrBuf<[u8; 16]>,
}
let r : &mut R = unsafe { transmute(&mut c) };
r.example.try_set(b"example").unwrap();
r.empty3 = Default::default();
assert_eq!(r.empty .is_empty(), true);
assert_eq!(r.empty2 .is_empty(), true);
assert_eq!(r.empty3 .is_empty(), true);
assert_eq!(r.example .is_empty(), false);
assert_eq!(r.full .is_empty(), false);
assert_eq!(r.not_unicode .is_empty(), false);
assert_eq!(r.empty .buffer(), b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
assert_eq!(r.empty2 .buffer(), b"\0fffffffffffffff");
assert_eq!(r.empty3 .buffer(), b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
assert_eq!(r.example .buffer(), b"example\0\0\0\0\0\0\0\0\0");
assert_eq!(r.full .buffer(), b"ffffffffffffffff");
assert_eq!(r.not_unicode .buffer(), b"\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
assert_eq!(r.empty .to_bytes(), b"");
assert_eq!(r.empty2 .to_bytes(), b"");
assert_eq!(r.empty3 .to_bytes(), b"");
assert_eq!(r.example .to_bytes(), b"example");
assert_eq!(r.full .to_bytes(), b"ffffffffffffffff");
assert_eq!(r.not_unicode .to_bytes(), b"\xFF\xFF");
assert_eq!(r.empty .to_bytes_with_nul(), Ok(&b"\0"[..]));
assert_eq!(r.empty2 .to_bytes_with_nul(), Ok(&b"\0"[..]));
assert_eq!(r.empty3 .to_bytes_with_nul(), Ok(&b"\0"[..]));
assert_eq!(r.example .to_bytes_with_nul(), Ok(&b"example\0"[..]));
assert_eq!(r.full .to_bytes_with_nul(), Err(NotNulTerminatedError(())));
assert_eq!(r.not_unicode .to_bytes_with_nul(), Ok(&b"\xFF\xFF\0"[..]));
assert_eq!(r.empty .to_cstr(), Ok(CStr::from_bytes_with_nul(b"\0").unwrap()));
assert_eq!(r.empty2 .to_cstr(), Ok(CStr::from_bytes_with_nul(b"\0").unwrap()));
assert_eq!(r.empty3 .to_cstr(), Ok(CStr::from_bytes_with_nul(b"\0").unwrap()));
assert_eq!(r.example .to_cstr(), Ok(CStr::from_bytes_with_nul(b"example\0").unwrap()));
assert_eq!(r.full .to_cstr(), Err(NotNulTerminatedError(())));
assert_eq!(r.not_unicode .to_cstr(), Ok(CStr::from_bytes_with_nul(b"\xFF\xFF\0").unwrap()));
assert_eq!(r.empty .to_str(), Ok(""));
assert_eq!(r.empty2 .to_str(), Ok(""));
assert_eq!(r.empty3 .to_str(), Ok(""));
assert_eq!(r.example .to_str(), Ok("example"));
assert_eq!(r.full .to_str(), Ok("ffffffffffffffff"));
assert_eq!(r.not_unicode .to_str().is_err(), true);
assert_eq!(r.empty .to_string_lossy(), "");
assert_eq!(r.empty2 .to_string_lossy(), "");
assert_eq!(r.empty3 .to_string_lossy(), "");
assert_eq!(r.example .to_string_lossy(), "example");
assert_eq!(r.full .to_string_lossy(), "ffffffffffffffff");
assert_eq!(r.not_unicode .to_string_lossy(), "\u{FFFD}\u{FFFD}");
assert_eq!(r.empty .validate().is_err(), false);
assert_eq!(r.empty2 .validate().is_err(), false);
assert_eq!(r.empty3 .validate().is_err(), false);
assert_eq!(r.example .validate().is_err(), false);
assert_eq!(r.full .validate().is_err(), true);
assert_eq!(r.not_unicode .validate().is_err(), false);
assert_eq!(format!("{:?}", r.empty ), "\"\"" );
assert_eq!(format!("{:?}", r.empty2 ), "\"\"" );
assert_eq!(format!("{:?}", r.empty3 ), "\"\"" );
assert_eq!(format!("{:?}", r.example ), "\"example\"" );
assert_eq!(format!("{:?}", r.full ), "\"ffffffffffffffff\"" );
assert_eq!(format!("{:?}", r.not_unicode ), "\"\\xff\\xff\"" );
unsafe {
assert_eq!(r.empty .buffer_mut(), b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
assert_eq!(r.empty2 .buffer_mut(), b"\0fffffffffffffff");
assert_eq!(r.empty3 .buffer_mut(), b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
assert_eq!(r.example .buffer_mut(), b"example\0\0\0\0\0\0\0\0\0");
assert_eq!(r.full .buffer_mut(), b"ffffffffffffffff");
assert_eq!(r.not_unicode .buffer_mut(), b"\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
}
assert_eq!(r.empty .nul_truncate().to_bytes(), b"");
assert_eq!(r.empty2 .nul_truncate().to_bytes(), b"");
assert_eq!(r.empty3 .nul_truncate().to_bytes(), b"");
assert_eq!(r.example .nul_truncate().to_bytes(), b"example");
assert_eq!(r.full .nul_truncate().to_bytes(), b"fffffffffffffff");
assert_eq!(r.not_unicode .nul_truncate().to_bytes(), b"\xFF\xFF");
assert_eq!(r.empty .buffer(), b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
assert_eq!(r.empty2 .buffer(), b"\0ffffffffffffff\0");
assert_eq!(r.empty3 .buffer(), b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
assert_eq!(r.example .buffer(), b"example\0\0\0\0\0\0\0\0\0");
assert_eq!(r.full .buffer(), b"fffffffffffffff\0");
assert_eq!(r.not_unicode .buffer(), b"\xFF\xFF\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
assert_eq!(r.empty .validate().is_err(), false);
assert_eq!(r.empty2 .validate().is_err(), false);
assert_eq!(r.empty3 .validate().is_err(), false);
assert_eq!(r.example .validate().is_err(), false);
assert_eq!(r.full .validate().is_err(), false);
assert_eq!(r.not_unicode .validate().is_err(), false);
}