use crate::Error;
use std::ffi::CString;
use std::os::raw::c_char;
use uplink_sys as ulksys;
pub fn cstring_from_str_fn_arg(arg_name: &str, arg_val: &str) -> Result<CString, Error> {
CString::new(arg_val).map_err(|e| {
Error::new_invalid_arguments(
arg_name,
&format!(
"cannot contains null bytes (0 byte). Null byte found at {}",
e.nul_position()
),
)
})
}
pub unsafe fn unchecked_ptr_c_char_and_length_to_string(
c_chars: *const c_char,
length: usize,
) -> String {
let mut chars = String::with_capacity(length);
for i in 0..length as isize {
chars.push(*c_chars.offset(i) as u8 as char)
}
chars
}
pub fn drop_uplink_sys_error(error: *mut ulksys::UplinkError) {
if !error.is_null() {
unsafe {
ulksys::uplink_free_error(error);
}
}
}
#[cfg(test)]
pub(crate) mod test {
pub(crate) fn assert_c_string(have: *const c_char, want: &str) {
if let Some((p, h, w)) = compare_c_string(have, want) {
panic!(
"unexpected character at position +{}. Want= {:?}, have= {:?}",
p, w as u8 as char, h as u8 as char,
);
}
}
fn assert_raw_pointer<T: std::cmp::Eq + Copy + std::fmt::Debug>(
have: *const T,
want: *const T,
want_length: usize,
) {
if let Some((p, h, w)) = compare_raw_pointers(have, want, want_length) {
panic!(
"unexpected value at memory position +{}. Want= {:?}, have= {:?}",
p, w, h
);
}
}
pub(crate) fn compare_c_string(
c_str: *const c_char,
r_str: &str,
) -> Option<(usize, c_char, c_char)> {
let c_r_str = CString::new(r_str).expect("want not having any null character");
compare_raw_pointers(c_str, c_r_str.as_ptr(), r_str.len())
}
pub(crate) fn compare_raw_pointers<T: std::cmp::Eq + Copy + std::fmt::Debug>(
a: *const T,
b: *const T,
length: usize,
) -> Option<(usize, T, T)> {
unsafe {
for i in 0..length {
let ai = *a.add(i);
let bi = *b.add(i);
if ai != bi {
return Some((i, ai, bi));
}
}
}
None
}
use super::*;
use std::ffi::CStr;
#[test]
fn test_cstring_from_str_fn_arg() {
let val = cstring_from_str_fn_arg("some", "this is fine")
.expect("returned error on a valid CString");
assert_eq!(
val,
CString::new("this is fine").unwrap(),
"returned a CString with an invalid value"
);
let err = cstring_from_str_fn_arg("some", "this is invalid\0 ")
.expect_err("returned Ok on an invalid CString");
if let Error::InvalidArguments(args) = err {
assert_eq!(
args.names, "some",
"invalid Error::InvalidArguments name field value"
);
assert_eq!(
args.msg, "cannot contains null bytes (0 byte). Null byte found at 15",
"invalid Error::InvalidArguments msg field value"
)
} else {
panic!("expected an Error::InvalidArguments");
}
}
#[test]
fn test_unchecked_ptr_c_char_and_length_to_string() {
unsafe {
{
let expected = String::from("Storj Uplink Rust");
let cstr = CStr::from_bytes_with_nul_unchecked(expected.as_bytes());
let chars = cstr.as_ptr();
assert_eq!(
unchecked_ptr_c_char_and_length_to_string(chars, expected.len()),
expected,
"str value doesn't match"
);
}
{
let expected = String::from("Storj Uplink Rust\0");
let cstr = CStr::from_bytes_with_nul_unchecked(expected.as_bytes());
let chars = cstr.as_ptr();
assert_eq!(
unchecked_ptr_c_char_and_length_to_string(chars, expected.len()).as_ref(),
expected,
"str value doesn't match"
);
}
{
let expected = String::from("Storj Uplink\0 Ru\0st");
let cstr = CStr::from_bytes_with_nul_unchecked(expected.as_bytes());
let chars = cstr.as_ptr();
assert_eq!(
unchecked_ptr_c_char_and_length_to_string(chars, expected.len()).as_ref(),
expected,
"str value doesn't match"
);
}
{
let expected = String::from("Storj Uplink\0 Ru\0st\0");
let cstr = CStr::from_bytes_with_nul_unchecked(expected.as_bytes());
let chars = cstr.as_ptr();
assert_eq!(
unchecked_ptr_c_char_and_length_to_string(chars, expected.len()).as_ref(),
expected,
"str value doesn't match"
);
}
{
let passed = String::from("Storj Uplink Rust");
let cstr = CStr::from_bytes_with_nul_unchecked(passed.as_bytes());
let chars = cstr.as_ptr();
let mut expected = passed.clone();
expected.truncate(passed.len() - 1);
assert_eq!(
unchecked_ptr_c_char_and_length_to_string(chars, passed.len() - 1),
expected,
"str value doesn't match"
);
}
{
let expected = String::from("Storj Uplink Rust OUT");
let cstr = CStr::from_bytes_with_nul_unchecked(expected.as_bytes());
let chars = cstr.as_ptr();
assert_eq!(
unchecked_ptr_c_char_and_length_to_string(chars, expected.len()),
expected,
"str value doesn't match"
);
}
{
let expected = String::from("Storj Uplink \u{FFFD} Rust");
let cstr = CStr::from_bytes_with_nul_unchecked(expected.as_bytes());
let chars = cstr.as_ptr();
assert_ne!(
unchecked_ptr_c_char_and_length_to_string(chars, expected.len()),
expected,
);
}
}
}
#[test]
fn test_assert_c_string() {
{
let empty = CString::new("").unwrap();
assert_c_string(empty.as_ptr(), "");
}
{
let word = CString::new("Rust").unwrap();
assert_c_string(word.as_ptr(), "Rust");
}
}
#[test]
#[should_panic]
fn test_assert_c_string_panic_shorter() {
let word = CString::new("Rust").unwrap();
assert_c_string(word.as_ptr(), "Rusty");
}
#[test]
#[should_panic = "unexpected character at position +1. Want= 'u', have= 'o'"]
fn test_assert_c_string_panic_unmatch() {
let word = CString::new("Rost").unwrap();
assert_c_string(word.as_ptr(), "Rust");
}
#[test]
fn test_assert_raw_pointer() {
let want = vec![10, 20, 30];
let have = want.clone();
assert_raw_pointer(have.as_ptr(), want.as_ptr(), want.len());
assert_raw_pointer(want.as_ptr(), want.as_ptr(), want.len());
}
#[test]
#[should_panic = "unexpected value at memory position +3. Want= 3, have= 6"]
fn test_assert_raw_pointer_panic_unmatch() {
let have = vec![0, 1, 2, 6, 4];
let want = vec![0, 1, 2, 3, 4];
assert_raw_pointer(have.as_ptr(), want.as_ptr(), want.len());
}
}