From 3beadbb6cb4d5584adfe271d905618293cddd597 Mon Sep 17 00:00:00 2001 From: Stu Black Date: Fri, 3 Oct 2025 00:50:02 -0400 Subject: [PATCH] Forgot to add new array.rs impl. --- wutil-rs/src/array.rs | 447 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 447 insertions(+) create mode 100644 wutil-rs/src/array.rs diff --git a/wutil-rs/src/array.rs b/wutil-rs/src/array.rs new file mode 100644 index 00000000..89288e69 --- /dev/null +++ b/wutil-rs/src/array.rs @@ -0,0 +1,447 @@ +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) -> *mut c_void, + data: *mut c_void, + ) { + if array.is_null() { + return; + } + for a in unsafe { &mut (*array).items } { + *a = NonNull::new(unsafe { f(a.as_ptr(), data) }).unwrap(); + } + } + + #[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()); + } + } +}