use std::os::raw::c_char;
use crate::{c_ptr_to_string, string_to_c_ptr};
#[must_use]
pub fn lift_opt_string(ptr: *const c_char) -> Option<String> {
c_ptr_to_string(ptr)
}
#[must_use]
pub unsafe fn lift_bytes(ptr: *const u8, len: usize) -> Vec<u8> {
if ptr.is_null() || len == 0 {
return Vec::new();
}
unsafe { std::slice::from_raw_parts(ptr, len) }.to_vec()
}
#[must_use]
pub unsafe fn lift_byte_slice<'a>(ptr: *const u8, len: usize) -> &'a [u8] {
if ptr.is_null() || len == 0 {
return &[];
}
unsafe { std::slice::from_raw_parts(ptr, len) }
}
#[must_use]
pub unsafe fn lift_scalar_vec<T: Copy>(ptr: *const T, len: usize) -> Vec<T> {
if ptr.is_null() || len == 0 {
return Vec::new();
}
unsafe { std::slice::from_raw_parts(ptr, len) }.to_vec()
}
#[must_use]
pub unsafe fn lift_string_vec(ptr: *const *const c_char, len: usize) -> Vec<String> {
if ptr.is_null() || len == 0 {
return Vec::new();
}
let slots = unsafe { std::slice::from_raw_parts(ptr, len) };
slots
.iter()
.map(|&p| c_ptr_to_string(p).unwrap_or_default())
.collect()
}
#[must_use]
pub unsafe fn lift_ptr_vec<T: Clone>(ptr: *const *const T, len: usize) -> Vec<T> {
if ptr.is_null() || len == 0 {
return Vec::new();
}
let slots = unsafe { std::slice::from_raw_parts(ptr, len) };
slots
.iter()
.filter_map(|&p| {
if p.is_null() {
None
} else {
Some(unsafe { &*p }.clone())
}
})
.collect()
}
#[must_use]
pub unsafe fn lift_opt_scalar<T: Copy>(ptr: *const T) -> Option<T> {
if ptr.is_null() {
return None;
}
Some(unsafe { *ptr })
}
#[must_use]
pub fn lower_opt_string(value: Option<impl AsRef<str>>) -> *const c_char {
match value {
Some(s) => string_to_c_ptr(s),
None => std::ptr::null(),
}
}
pub unsafe fn lower_bytes(data: Vec<u8>, out_len: *mut usize) -> *const u8 {
if !out_len.is_null() {
unsafe { *out_len = data.len() };
}
if data.is_empty() {
return std::ptr::null();
}
let boxed = data.into_boxed_slice();
Box::into_raw(boxed) as *const u8
}
pub unsafe fn lower_scalar_vec<T>(data: Vec<T>, out_len: *mut usize) -> *mut T {
if !out_len.is_null() {
unsafe { *out_len = data.len() };
}
if data.is_empty() {
return std::ptr::null_mut();
}
let boxed = data.into_boxed_slice();
Box::into_raw(boxed) as *mut T
}
pub unsafe fn lower_string_vec(data: Vec<String>, out_len: *mut usize) -> *mut *const c_char {
let ptrs: Vec<*const c_char> = data.iter().map(string_to_c_ptr).collect();
unsafe { lower_scalar_vec(ptrs, out_len) }
}
pub unsafe fn lower_ptr_vec<T>(data: Vec<*mut T>, out_len: *mut usize) -> *mut *mut T {
unsafe { lower_scalar_vec(data, out_len) }
}
#[must_use]
pub fn lower_opt_scalar<T>(value: Option<T>) -> *mut T {
match value {
Some(v) => Box::into_raw(Box::new(v)),
None => std::ptr::null_mut(),
}
}
pub unsafe fn write_map_out<K, V>(
keys: *mut K,
values: *mut V,
len: usize,
out_keys: *mut *mut K,
out_values: *mut *mut V,
out_len: *mut usize,
) {
if !out_keys.is_null() {
unsafe { *out_keys = keys };
}
if !out_values.is_null() {
unsafe { *out_values = values };
}
if !out_len.is_null() {
unsafe { *out_len = len };
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{free_bytes, free_string};
#[test]
fn opt_string_roundtrip() {
assert_eq!(lift_opt_string(std::ptr::null()), None);
let p = string_to_c_ptr("hi");
assert_eq!(lift_opt_string(p), Some("hi".to_string()));
free_string(p);
}
#[test]
fn bytes_roundtrip() {
let data = vec![1u8, 2, 3, 4];
let mut len = 0usize;
let ptr = unsafe { lower_bytes(data.clone(), &mut len) } as *mut u8;
assert_eq!(len, 4);
let back = unsafe { lift_bytes(ptr, len) };
assert_eq!(back, data);
free_bytes(ptr, len);
}
#[test]
fn empty_bytes_is_null() {
let mut len = 99usize;
let ptr = unsafe { lower_bytes(Vec::new(), &mut len) };
assert!(ptr.is_null());
assert_eq!(len, 0);
}
#[test]
fn scalar_vec_roundtrip() {
let data = vec![10i32, 20, 30];
let mut len = 0usize;
let ptr = unsafe { lower_scalar_vec(data.clone(), &mut len) };
assert_eq!(len, 3);
let back = unsafe { lift_scalar_vec(ptr, len) };
assert_eq!(back, data);
free_bytes(ptr as *mut u8, len * std::mem::size_of::<i32>());
}
#[test]
fn string_vec_roundtrip() {
let data = vec!["a".to_string(), "bb".to_string()];
let mut len = 0usize;
let ptr = unsafe { lower_string_vec(data.clone(), &mut len) };
assert_eq!(len, 2);
let back = unsafe { lift_string_vec(ptr, len) };
assert_eq!(back, data);
let slots = unsafe { std::slice::from_raw_parts(ptr, len) };
for &p in slots {
free_string(p);
}
free_bytes(ptr as *mut u8, len * std::mem::size_of::<*const c_char>());
}
#[test]
fn opt_scalar_roundtrip() {
assert_eq!(unsafe { lift_opt_scalar::<i32>(std::ptr::null()) }, None);
let p = lower_opt_scalar(Some(7i32));
assert_eq!(unsafe { lift_opt_scalar(p) }, Some(7));
unsafe { drop(Box::from_raw(p)) };
assert!(lower_opt_scalar::<i32>(None).is_null());
}
#[test]
fn ptr_vec_lifts_and_skips_nulls() {
#[derive(Clone, PartialEq, Debug)]
struct Item {
n: i32,
}
let a = Box::into_raw(Box::new(Item { n: 1 }));
let b = Box::into_raw(Box::new(Item { n: 2 }));
let slots: [*const Item; 3] = [a, std::ptr::null(), b];
let v = unsafe { lift_ptr_vec(slots.as_ptr(), slots.len()) };
assert_eq!(v, vec![Item { n: 1 }, Item { n: 2 }]);
unsafe {
drop(Box::from_raw(a));
drop(Box::from_raw(b));
}
let empty = unsafe { lift_ptr_vec::<Item>(std::ptr::null(), 3) };
assert!(empty.is_empty());
}
#[test]
fn lift_byte_slice_is_borrow() {
let data = [9u8, 8, 7];
let s = unsafe { lift_byte_slice(data.as_ptr(), data.len()) };
assert_eq!(s, &data);
let empty = unsafe { lift_byte_slice::<'static>(std::ptr::null(), 0) };
assert!(empty.is_empty());
}
}