450 lines
12 KiB
Rust
450 lines
12 KiB
Rust
use std::{ffi::c_void, ptr::NonNull};
|
|
|
|
pub struct Array {
|
|
items: Vec<NonNull<c_void>>,
|
|
destructor: Option<unsafe extern "C" fn(x: *mut c_void)>,
|
|
}
|
|
|
|
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<unsafe extern "C" fn(item: *const c_void, cdata: *mut c_void) -> 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<unsafe extern "C" fn(item: *const c_void, cdata: *mut c_void) -> 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::<c_void>());
|
|
assert_eq!(WMGetArrayItemCount(array), 1);
|
|
WMEmptyArray(array);
|
|
assert!(ptr::eq(SENTINEL, (&x as *const u32).cast::<c_void>()));
|
|
assert_eq!(0xdeadbeefu32, *SENTINEL.cast::<u32>());
|
|
|
|
SENTINEL = ptr::null_mut();
|
|
WMFreeArray(array);
|
|
assert!(SENTINEL.is_null());
|
|
}
|
|
}
|
|
}
|