use crate::{fz_string_t, FzString};
use ffizz_passby::OpaqueStruct;
use libc::c_char;
use std::ffi::{CStr, CString};
#[no_mangle]
pub unsafe extern "C" fn fz_string_borrow(cstr: *const c_char) -> fz_string_t {
debug_assert!(!cstr.is_null());
let cstr: &CStr = unsafe { CStr::from_ptr(cstr) };
unsafe { FzString::return_val(FzString::CStr(cstr)) }
}
#[allow(clippy::missing_safety_doc)] #[no_mangle]
pub unsafe extern "C" fn fz_string_null() -> fz_string_t {
unsafe { FzString::return_val(FzString::Null) }
}
#[no_mangle]
pub unsafe extern "C" fn fz_string_clone(cstr: *const c_char) -> fz_string_t {
debug_assert!(!cstr.is_null());
let cstr: &CStr = unsafe { CStr::from_ptr(cstr) };
let cstring: CString = cstr.into();
unsafe { FzString::return_val(FzString::CString(cstring)) }
}
#[no_mangle]
pub unsafe extern "C" fn fz_string_clone_with_len(buf: *const c_char, len: usize) -> fz_string_t {
debug_assert!(!buf.is_null());
debug_assert!(len < isize::MAX as usize);
let slice = unsafe { std::slice::from_raw_parts(buf as *const u8, len) };
let vec = slice.to_vec();
unsafe { FzString::return_val(FzString::Bytes(vec)) }
}
#[no_mangle]
pub unsafe extern "C" fn fz_string_content(fzs: *mut fz_string_t) -> *const c_char {
unsafe {
FzString::with_ref_mut(fzs, |fzs| match fzs.as_cstr() {
Ok(Some(cstr)) => cstr.as_ptr(),
_ => std::ptr::null(),
})
}
}
#[no_mangle]
pub unsafe extern "C" fn fz_string_content_with_len(
fzs: *mut fz_string_t,
len_out: *mut usize,
) -> *const c_char {
unsafe {
FzString::with_ref_mut(fzs, |fzs| {
let bytes = match fzs.as_bytes() {
Some(bytes) => bytes,
None => {
unsafe {
*len_out = 0;
}
return std::ptr::null();
}
};
unsafe {
*len_out = bytes.len();
}
bytes.as_ptr() as *const c_char
})
}
}
#[allow(clippy::missing_safety_doc)] #[no_mangle]
pub unsafe extern "C" fn fz_string_is_null(fzs: *const fz_string_t) -> bool {
unsafe { FzString::with_ref(fzs, |fzs| fzs.is_null()) }
}
#[no_mangle]
pub unsafe extern "C" fn fz_string_free(fzs: *mut fz_string_t) {
drop(unsafe { FzString::take(fzs) });
}
#[cfg(test)]
mod test {
use super::*;
const INVALID_UTF8: &[u8] = b"abc\xf0\x28\x8c\x28";
#[test]
fn borrow() {
let s = CString::new("hello!").unwrap();
let ptr = unsafe { s.as_ptr() };
let mut fzs = unsafe { fz_string_borrow(ptr) };
assert!(unsafe { !fz_string_is_null(&fzs as *const fz_string_t) });
let content = unsafe { CStr::from_ptr(fz_string_content(&mut fzs as *mut fz_string_t)) };
assert_eq!(content.to_str().unwrap(), "hello!");
drop(s);
unsafe { fz_string_free(&mut fzs as *mut fz_string_t) };
}
#[test]
fn borrow_invalid_utf8() {
let s = CString::new(INVALID_UTF8).unwrap();
let ptr = unsafe { s.as_ptr() };
let mut fzs = unsafe { fz_string_borrow(ptr) };
assert!(unsafe { !fz_string_is_null(&fzs as *const fz_string_t) });
let mut len: usize = 0;
let ptr = unsafe {
fz_string_content_with_len(&mut fzs as *mut fz_string_t, &mut len as *mut usize)
};
let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, len) };
assert_eq!(slice, INVALID_UTF8);
drop(s);
unsafe { fz_string_free(&mut fzs as *mut fz_string_t) };
}
#[test]
fn clone() {
let s = CString::new("hello!").unwrap();
let ptr = unsafe { s.as_ptr() };
let mut fzs = unsafe { fz_string_clone(ptr) };
assert!(unsafe { !fz_string_is_null(&fzs as *const fz_string_t) });
drop(s);
let content = unsafe { CStr::from_ptr(fz_string_content(&mut fzs as *mut fz_string_t)) };
assert_eq!(content.to_str().unwrap(), "hello!");
unsafe { fz_string_free(&mut fzs as *mut fz_string_t) };
}
#[test]
fn null_and_is_null() {
let mut fzs = unsafe { fz_string_null() };
assert!(unsafe { fz_string_is_null(&fzs as *const fz_string_t) });
unsafe { fz_string_free(&mut fzs as *mut fz_string_t) };
}
#[test]
fn null_ptr_is_null() {
let mut fzs = unsafe { fz_string_null() };
assert!(unsafe { fz_string_is_null(std::ptr::null()) });
unsafe { fz_string_free(&mut fzs as *mut fz_string_t) };
}
#[test]
fn clone_invalid_utf8() {
let s = CString::new(INVALID_UTF8).unwrap();
let ptr = unsafe { s.as_ptr() };
let mut fzs = unsafe { fz_string_clone(ptr) };
assert!(unsafe { !fz_string_is_null(&fzs as *const fz_string_t) });
drop(s);
let mut len: usize = 0;
let ptr = unsafe {
fz_string_content_with_len(&mut fzs as *mut fz_string_t, &mut len as *mut usize)
};
let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, len) };
assert_eq!(slice, INVALID_UTF8);
unsafe { fz_string_free(&mut fzs as *mut fz_string_t) };
}
#[test]
fn clone_with_len() {
let s = CString::new("ABCDEFGH").unwrap();
let ptr = unsafe { s.as_ptr() };
let mut fzs = unsafe { fz_string_clone_with_len(ptr, 4) };
assert!(unsafe { !fz_string_is_null(&fzs as *const fz_string_t) });
drop(s);
let content = unsafe { CStr::from_ptr(fz_string_content(&mut fzs as *mut fz_string_t)) };
assert_eq!(content.to_str().unwrap(), "ABCD");
unsafe { fz_string_free(&mut fzs as *mut fz_string_t) };
}
#[test]
fn clone_with_len_invalid_utf8() {
let s = CString::new(INVALID_UTF8).unwrap();
let ptr = unsafe { s.as_ptr() };
let mut fzs = unsafe { fz_string_clone_with_len(ptr, 4) };
assert!(unsafe { !fz_string_is_null(&fzs as *const fz_string_t) });
drop(s);
let mut len: usize = 0;
let ptr = unsafe {
fz_string_content_with_len(&mut fzs as *mut fz_string_t, &mut len as *mut usize)
};
let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, len) };
assert_eq!(slice, &INVALID_UTF8[..4]);
unsafe { fz_string_free(&mut fzs as *mut fz_string_t) };
}
#[test]
fn content_nul_bytes() {
let s = String::from("hello \0 NUL byte");
let ptr = unsafe { s.as_ptr() } as *mut c_char;
let mut fzs = unsafe { fz_string_clone_with_len(ptr, s.len()) };
assert!(unsafe { !fz_string_is_null(&fzs as *const fz_string_t) });
let ptr = unsafe { fz_string_content(&mut fzs as *mut fz_string_t) };
assert!(ptr.is_null());
unsafe { fz_string_free(&mut fzs as *mut fz_string_t) };
}
#[test]
fn content_null_ptr() {
let ptr = unsafe { fz_string_content(std::ptr::null_mut()) };
assert!(ptr.is_null());
}
#[test]
fn content_with_len_nul_bytes() {
let s = String::from("hello \0 NUL byte");
let ptr = unsafe { s.as_ptr() } as *mut c_char;
let mut fzs = unsafe { fz_string_clone_with_len(ptr, s.len()) };
assert!(unsafe { !fz_string_is_null(&fzs as *const fz_string_t) });
let mut len: usize = 0;
let ptr = unsafe {
fz_string_content_with_len(&mut fzs as *mut fz_string_t, &mut len as *mut usize)
};
let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, len) };
let s = std::str::from_utf8(slice).unwrap();
assert_eq!(s, "hello \0 NUL byte");
unsafe { fz_string_free(&mut fzs as *mut fz_string_t) };
}
#[test]
fn content_with_len_null_ptr() {
let mut len: usize = 9999;
let ptr =
unsafe { fz_string_content_with_len(std::ptr::null_mut(), &mut len as *mut usize) };
assert!(ptr.is_null());
assert_eq!(len, 0);
}
}