use super::JvmError;
use std::{
borrow::Cow,
ffi::{CStr, CString},
};
pub(super) unsafe fn bytes_to_cstr<'a>(
mut s: Cow<'a, [u8]>,
original: Option<Cow<'_, str>>,
) -> Result<Cow<'a, CStr>, JvmError> {
let mut null_byte_added = false;
if s.last() != Some(&0) {
s.to_mut().push(0);
null_byte_added = true;
}
let convert_error = move |s: Cow<'a, [u8]>| -> JvmError {
let s: String = {
if let Some(original) = original {
original.into_owned()
} else {
let mut s: Vec<u8> = s.into_owned();
if null_byte_added {
let _removed_null_byte: Option<u8> = s.pop();
debug_assert_eq!(_removed_null_byte, Some(0));
}
unsafe { String::from_utf8_unchecked(s) }
}
};
JvmError::NullOptString(s)
};
let s: Cow<'a, CStr> = match s {
Cow::Owned(s) => Cow::Owned({
CString::from_vec_with_nul(s)
.map_err(|error| convert_error(Cow::Owned(error.into_bytes())))?
}),
Cow::Borrowed(s) => Cow::Borrowed({
CStr::from_bytes_with_nul(s).map_err(|_error| convert_error(Cow::Borrowed(s)))?
}),
};
Ok(s)
}
pub(super) fn utf8_to_cstr<'a>(s: Cow<'a, str>) -> Result<Cow<'a, CStr>, JvmError> {
let s: Cow<'a, [u8]> = match s {
Cow::Owned(s) => Cow::Owned(s.into_bytes()),
Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()),
};
unsafe { bytes_to_cstr(s, None) }
}
#[test]
fn test() {
use assert_matches::assert_matches;
{
let result = utf8_to_cstr("Hello, world 😎".into()).unwrap();
assert_eq!(
result.to_bytes_with_nul(),
b"Hello, world \xf0\x9f\x98\x8e\0"
);
assert_matches!(result, Cow::Owned(_));
}
{
let result = utf8_to_cstr("Hello, world 😎\0".into()).unwrap();
assert_eq!(
result.to_bytes_with_nul(),
b"Hello, world \xf0\x9f\x98\x8e\0"
);
assert_matches!(result, Cow::Borrowed(_));
}
{
let result = utf8_to_cstr("Hello,\0world".into()).unwrap_err();
let error_string = assert_matches!(result, JvmError::NullOptString(string) => string);
assert_eq!(error_string, "Hello,\0world");
}
}