use std::{ffi::c_void, ptr::NonNull}; pub struct Array { items: Vec>, destructor: Option, } pub mod ffi { use super::Array; use std::{ ffi::{c_int, c_void}, ptr::{self, NonNull}, }; pub const NOT_FOUND: c_int = -1; #[unsafe(no_mangle)] pub unsafe extern "C" fn WMCreateArray(initial_size: c_int) -> *mut Array { let cap = if initial_size < 0 { 0 } else { initial_size as usize }; Box::leak(Box::new(Array { items: Vec::with_capacity(cap), destructor: None, })) } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMCreateArrayWithDestructor( initial_size: c_int, destructor: unsafe extern "C" fn(x: *mut c_void), ) -> *mut Array { let cap = if initial_size < 0 { 0 } else { initial_size as usize }; Box::leak(Box::new(Array { items: Vec::with_capacity(cap), destructor: Some(destructor), })) } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMCreateArrayWithArray(array: *mut Array) -> *mut Array { if array.is_null() { return ptr::null_mut(); } let array = unsafe { &*array }; Box::leak(Box::new(Array { items: array.items.clone(), destructor: array.destructor, })) } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMEmptyArray(array: *mut Array) { if array.is_null() { return; } let array = unsafe { &mut *array }; if let Some(f) = array.destructor { for item in &mut array.items { unsafe { (f)(item.as_ptr()) } } } array.items.clear(); } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMFreeArray(array: *mut Array) { if array.is_null() { return; } unsafe { WMEmptyArray(array); let _ = ptr::read(array); } } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMGetArrayItemCount(array: *mut Array) -> c_int { if array.is_null() { return 0; } unsafe { (*array).items.len() as c_int } } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMAddToArray(array: *mut Array, item: *mut c_void) { if array.is_null() { return; } if let Some(item) = NonNull::new(item) { unsafe { (*array).items.push(item); } } } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMInsertInArray(array: *mut Array, index: c_int, item: *mut c_void) { if array.is_null() { return; } if index < 0 { return; } let array = unsafe { &mut (*array).items }; let index = index as usize; if index >= array.len() { return; } if let Some(item) = NonNull::new(item) { array.insert(index, item); } } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMSetInArray( array: *mut Array, index: c_int, item: *mut c_void, ) -> *mut c_void { if array.is_null() { return ptr::null_mut(); } if index < 0 { return ptr::null_mut(); } let index = index as usize; /* is it really useful to perform append if index == array->itemCount ? -Dan */ if index == unsafe { (*array).items.len() } { unsafe { WMAddToArray(array, item); } return ptr::null_mut(); } let item = match NonNull::new(item) { Some(x) => x, None => return ptr::null_mut(), }; let array = unsafe { &mut (*array).items }; let old = array[index]; array[index] = item; old.as_ptr() } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMDeleteFromArray(array: *mut Array, index: c_int) -> c_int { if array.is_null() { return 0; } let array = unsafe { &mut *array }; if index < 0 { return 0; } let index = index as usize; if index >= array.items.len() { 0 } else { let old = array.items.remove(index); if let Some(f) = array.destructor { unsafe { (f)(old.as_ptr()); } } 1 } } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMRemoveFromArray(array: *mut Array, item: *mut c_void) -> c_int { unsafe { WMRemoveFromArrayMatching(array, None, item) } } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMRemoveFromArrayMatching( array: *mut Array, pred: Option c_int>, cdata: *mut c_void, ) -> c_int { if array.is_null() { return 1; } let array = unsafe { &mut *array }; let original_len = array.items.len(); match pred { Some(f) => array.items.retain(|x| unsafe { f(x.as_ptr(), cdata) != 0 }), None => array.items.retain(|x| ptr::eq(x.as_ptr(), cdata)), } (original_len - array.items.len()) as c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMGetFromArray(array: *mut Array, index: c_int) -> *mut c_void { if array.is_null() || index < 0 { return ptr::null_mut(); } unsafe { (&(*array)) .items .get(index as usize) .map(|p| p.as_ptr()) .unwrap_or(ptr::null_mut()) } } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMGetFirstInArray(array: *mut Array, item: *mut c_void) -> c_int { unsafe { WMFindInArray(array, None, item) } } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMPopFromArray(array: *mut Array) -> *mut c_void { if array.is_null() { return ptr::null_mut(); } unsafe { (*array) .items .pop() .map(|p| p.as_ptr()) .unwrap_or(ptr::null_mut()) } } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMFindInArray( array: *mut Array, pred: Option c_int>, cdata: *mut c_void, ) -> c_int { if array.is_null() { return NOT_FOUND; } let array = unsafe { &*array }; if let Some(f) = pred { array .items .iter() .enumerate() .find(|(_, item)| unsafe { f(item.as_ptr(), cdata) != 0 }) .map(|(i, _)| i as c_int) .unwrap_or(NOT_FOUND) } else { array .items .iter() .enumerate() .find(|(_, item)| ptr::eq(item.as_ptr(), cdata)) .map(|(i, _)| i as c_int) .unwrap_or(NOT_FOUND) } } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMCountInArray(array: *mut Array, item: *const c_void) -> c_int { if array.is_null() { return 0; } let array = unsafe { &*array }; array .items .iter() .filter(|x| ptr::eq(x.as_ptr(), item)) .count() as c_int } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMSortArray( array: *mut Array, comparator: unsafe extern "C" fn(a: *const c_void, b: *const c_void) -> c_int, ) { if array.is_null() { return; } unsafe { (*array) .items .sort_by(|&a, &b| match comparator(a.as_ptr(), b.as_ptr()).signum() { -1 => std::cmp::Ordering::Less, 0 => std::cmp::Ordering::Equal, 1 => std::cmp::Ordering::Greater, _ => unreachable!(), }) } } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMMapArray( array: *mut Array, f: unsafe extern "C" fn(*mut c_void, *mut c_void), data: *mut c_void, ) { if array.is_null() { return; } unsafe { for a in &mut (*array).items { (f)(a.as_ptr(), data); } } } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMArrayFirst(array: *mut Array, iter: *mut c_int) -> *mut c_void { if array.is_null() || iter.is_null() { return ptr::null_mut(); } let array = unsafe { &*array }; match array.items.get(0) { None => { unsafe { *iter = NOT_FOUND; } ptr::null_mut() } Some(x) => { unsafe { *iter = 0; } x.as_ptr() } } } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMArrayLast(array: *mut Array, iter: *mut c_int) -> *mut c_void { if array.is_null() || iter.is_null() { return ptr::null_mut(); } let array = unsafe { &*array }; match array.items.last() { None => { unsafe { *iter = NOT_FOUND; } ptr::null_mut() } Some(x) => { unsafe { *iter = (array.items.len() - 1) as c_int; } x.as_ptr() } } } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMArrayNext(array: *mut Array, iter: *mut c_int) -> *mut c_void { if array.is_null() || iter.is_null() { return ptr::null_mut(); } let array = unsafe { &*array }; let index = unsafe { *iter }; if index < 0 { return ptr::null_mut(); } match array.items.get(index as usize) { Some(i) => { unsafe { *iter += 1; } i.as_ptr() } None => { unsafe { *iter = NOT_FOUND; } ptr::null_mut() } } } #[unsafe(no_mangle)] pub unsafe extern "C" fn WMArrayPrevious(array: *mut Array, iter: *mut c_int) -> *mut c_void { if array.is_null() || iter.is_null() { return ptr::null_mut(); } let array = unsafe { &*array }; let index = unsafe { *iter }; if index < 0 { return ptr::null_mut(); } match array.items.get(index as usize) { Some(i) => { unsafe { *iter -= 1; } i.as_ptr() } None => { unsafe { *iter = NOT_FOUND; } ptr::null_mut() } } } } #[cfg(test)] mod test { use std::{ffi::c_void, ptr}; use super::ffi::*; #[test] fn create_destroy_with_size() { unsafe { let array = WMCreateArray(10); assert_eq!((*array).items.len(), 0); assert!((*array).items.capacity() >= 10); WMFreeArray(array); } } #[test] fn create_push_clear_destroy() { static mut SENTINEL: *mut c_void = ptr::null_mut(); unsafe extern "C" fn destructor(item: *mut c_void) { unsafe { SENTINEL = item; } } unsafe { let array = WMCreateArrayWithDestructor(10, destructor); assert!(SENTINEL.is_null()); let mut x = 0xdeadbeefu32; WMAddToArray(array, (&mut x as *mut u32).cast::()); assert_eq!(WMGetArrayItemCount(array), 1); WMEmptyArray(array); assert!(ptr::eq(SENTINEL, (&x as *const u32).cast::())); assert_eq!(0xdeadbeefu32, *SENTINEL.cast::()); SENTINEL = ptr::null_mut(); WMFreeArray(array); assert!(SENTINEL.is_null()); } } }