#![cfg_attr(all(doc, not(doctest)), feature(doc_auto_cfg))]
#![cfg_attr(all(doc, not(doctest)), feature(doc_cfg))]
#![warn(rustdoc::broken_intra_doc_links)]
use std::ffi::c_int;
#[repr(C)]
pub struct DynamicBufferView {
pub pointer: *const u8,
pub length: usize,
}
impl DynamicBufferView {
pub unsafe fn as_slice(&self) -> &[u8] {
std::slice::from_raw_parts(self.pointer, self.length)
}
}
impl From<&[u8]> for DynamicBufferView {
fn from(a: &[u8]) -> Self {
Self {
pointer: a.as_ptr(),
length: a.len(),
}
}
}
#[repr(C)]
pub struct DynamicBuffer {
pub pointer: *mut u8,
pub length: usize,
pub destructor: Option<unsafe extern "C" fn(*mut u8, usize) -> c_int>,
}
impl DynamicBuffer {
pub unsafe fn destroy(&mut self) -> Result<(), &str> {
if self.pointer.is_null() {
self.length = 0;
self.destructor = None;
return Ok(());
}
match self.destructor {
Some(destructor_callback) => {
let res = destructor_callback(self.pointer, self.length);
if res == 0 {
self.pointer = std::ptr::null_mut();
self.length = 0;
self.destructor = None;
return Ok(());
}
Err("destructor returned a non 0 error code")
}
None => Err("destructor is NULL, could not destroy DynamicBuffer"),
}
}
}
unsafe extern "C" fn free_u8_ptr_built_from_vec_u8(pointer: *mut u8, length: usize) -> c_int {
if pointer.is_null() {
return 0;
}
let slice = std::slice::from_raw_parts_mut(pointer, length);
drop(Box::from_raw(slice));
0
}
impl From<Vec<u8>> for DynamicBuffer {
fn from(value: Vec<u8>) -> Self {
let boxed_slice = value.into_boxed_slice();
let length = boxed_slice.len();
let pointer = Box::leak(boxed_slice);
Self {
pointer: pointer.as_mut_ptr(),
length,
destructor: Some(free_u8_ptr_built_from_vec_u8),
}
}
}
fn check_ptr_is_non_null_and_aligned<T>(ptr: *const T) -> Result<(), String> {
if ptr.is_null() {
return Err(format!("pointer is null, got: {ptr:p}"));
}
let expected_alignment = std::mem::align_of::<T>();
if ptr as usize % expected_alignment != 0 {
return Err(format!(
"pointer is misaligned, expected {expected_alignment} bytes alignment, got pointer: \
{ptr:p}. You May have mixed some pointers in your function call.",
));
}
Ok(())
}
unsafe fn get_mut_checked<'a, T>(ptr: *mut T) -> Result<&'a mut T, String> {
match check_ptr_is_non_null_and_aligned(ptr) {
Ok(()) => ptr
.as_mut()
.ok_or_else(|| "Error while converting to mut reference".into()),
Err(e) => Err(e),
}
}
#[no_mangle]
pub unsafe extern "C" fn destroy_dynamic_buffer(dynamic_buffer: *mut DynamicBuffer) -> c_int {
if dynamic_buffer.is_null() {
return 0;
}
let dynamic_buffer = match get_mut_checked(dynamic_buffer) {
Ok(dynamic_buffer) => dynamic_buffer,
Err(cause) => {
if cfg!(feature = "c_api_print_error_source") {
println!("{cause}");
}
return 1;
}
};
match dynamic_buffer.destroy() {
Ok(_) => 0,
Err(cause) => {
if cfg!(feature = "c_api_print_error_source") {
println!("{cause}");
}
1
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_dynamic_buffer_vec_u8_custom_destructor() {
let vec = vec![99u8; 1000];
let len = vec.len();
let ptr = vec.leak();
unsafe extern "C" fn custom_destroy_vec_u8_buffer(
pointer: *mut u8,
length: usize,
) -> c_int {
if pointer.is_null() {
return 0;
}
let slice = std::slice::from_raw_parts_mut(pointer, length);
drop(Box::from_raw(slice));
0
}
let mut dynamic_buffer = DynamicBuffer {
pointer: ptr.as_mut_ptr(),
length: len,
destructor: Some(custom_destroy_vec_u8_buffer),
};
let res = unsafe { destroy_dynamic_buffer(&mut dynamic_buffer as *mut DynamicBuffer) };
assert_eq!(res, 0);
assert!(dynamic_buffer.pointer.is_null());
assert_eq!(dynamic_buffer.length, 0);
assert!(dynamic_buffer.destructor.is_none());
assert!(dynamic_buffer.pointer.is_null());
let res = unsafe { destroy_dynamic_buffer(&mut dynamic_buffer as *mut DynamicBuffer) };
assert_eq!(res, 0);
assert!(dynamic_buffer.pointer.is_null());
assert_eq!(dynamic_buffer.length, 0);
assert!(dynamic_buffer.destructor.is_none());
let mut some_u8 = 0u8;
dynamic_buffer.pointer = &mut some_u8 as *mut u8;
assert!(dynamic_buffer.destructor.is_none());
let res = unsafe { destroy_dynamic_buffer(&mut dynamic_buffer as *mut DynamicBuffer) };
assert_eq!(res, 1);
}
#[test]
fn test_dynamic_buffer_vec_u8_default_destructor() {
let vec = vec![99u8; 1000];
let mut dynamic_buffer: DynamicBuffer = vec.clone().into();
let res = unsafe { destroy_dynamic_buffer(&mut dynamic_buffer as *mut DynamicBuffer) };
assert_eq!(res, 0);
assert!(dynamic_buffer.pointer.is_null());
assert_eq!(dynamic_buffer.length, 0);
assert!(dynamic_buffer.destructor.is_none());
let mut dynamic_buffer: DynamicBuffer = vec.into();
let res = unsafe { dynamic_buffer.destroy() };
assert!(res.is_ok());
assert!(dynamic_buffer.pointer.is_null());
assert_eq!(dynamic_buffer.length, 0);
assert!(dynamic_buffer.destructor.is_none());
}
}