Start addressing some bugs in new Rust code #10
@@ -39,7 +39,7 @@ ACLOCAL_AMFLAGS = -I m4
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = --enable-silent-rules LINGUAS='*'
|
||||
|
||||
|
||||
SUBDIRS = wrlib wutil-rs WINGs wmaker-rs src util po WindowMaker wmlib WPrefs.app doc
|
||||
SUBDIRS = wrlib wutil-rs wings-rs WINGs wmaker-rs src util po WindowMaker wmlib WPrefs.app doc
|
||||
DIST_SUBDIRS = $(SUBDIRS) test
|
||||
|
||||
EXTRA_DIST = TODO BUGS BUGFORM FAQ INSTALL \
|
||||
|
||||
@@ -156,12 +156,7 @@ typedef struct {
|
||||
|
||||
|
||||
|
||||
/* DO NOT ACCESS THE CONTENTS OF THIS STRUCT */
|
||||
typedef struct {
|
||||
void *table;
|
||||
void *nextItem;
|
||||
int index;
|
||||
} WMHashEnumerator;
|
||||
typedef struct WMHashEnumerator WMHashEnumerator;
|
||||
|
||||
|
||||
typedef struct {
|
||||
@@ -334,7 +329,7 @@ void WMDeleteInputHandler(WMHandlerID handlerID);
|
||||
|
||||
void WHandleEvents(void);
|
||||
|
||||
/* ---[ WINGs/hashtable.c ]----------------------------------------------- */
|
||||
/* ---[ wutil-rs/src/hash_table.rs ]----------------------------------------------- */
|
||||
|
||||
|
||||
WMHashTable* WMCreateIdentityHashTable();
|
||||
@@ -366,7 +361,7 @@ void* WMHashInsert(WMHashTable *table, const void *key, const void *data);
|
||||
void WMHashRemove(WMHashTable *table, const void *key);
|
||||
|
||||
/* warning: do not manipulate the table while using the enumerator functions */
|
||||
WMHashEnumerator WMEnumerateHashTable(WMHashTable *table);
|
||||
WMHashEnumerator* WMEnumerateHashTable(WMHashTable *table);
|
||||
|
||||
void* WMNextHashEnumeratorItem(WMHashEnumerator *enumerator);
|
||||
|
||||
@@ -381,7 +376,7 @@ Bool WMNextHashEnumeratorItemAndKey(WMHashEnumerator *enumerator,
|
||||
void **item, void **key);
|
||||
|
||||
|
||||
|
||||
void WMFreeHashEnumerator(WMHashEnumerator *enumerator);
|
||||
|
||||
/* some predefined callback sets */
|
||||
|
||||
|
||||
@@ -456,12 +456,12 @@ static void handleEvents(XEvent * event, void *data)
|
||||
|
||||
static void destroyBalloon(Balloon * bPtr)
|
||||
{
|
||||
WMHashEnumerator e;
|
||||
WMHashEnumerator *e;
|
||||
char *str;
|
||||
|
||||
e = WMEnumerateHashTable(bPtr->table);
|
||||
|
||||
while ((str = WMNextHashEnumeratorItem(&e))) {
|
||||
while ((str = WMNextHashEnumeratorItem(e))) {
|
||||
wfree(str);
|
||||
}
|
||||
WMFreeHashTable(bPtr->table);
|
||||
@@ -472,5 +472,7 @@ static void destroyBalloon(Balloon * bPtr)
|
||||
if (bPtr->font)
|
||||
WMReleaseFont(bPtr->font);
|
||||
|
||||
WMFreeHashEnumerator(e);
|
||||
|
||||
wfree(bPtr);
|
||||
}
|
||||
|
||||
@@ -520,7 +520,7 @@ static void listFamilies(WMScreen * scr, WMFontPanel * panel)
|
||||
FcFontSet *fs;
|
||||
FcPattern *pat;
|
||||
WMHashTable *families;
|
||||
WMHashEnumerator enumer;
|
||||
WMHashEnumerator *enumer;
|
||||
WMArray *array;
|
||||
int i;
|
||||
|
||||
@@ -551,7 +551,8 @@ static void listFamilies(WMScreen * scr, WMFontPanel * panel)
|
||||
|
||||
enumer = WMEnumerateHashTable(families);
|
||||
|
||||
while ((array = WMNextHashEnumeratorItem(&enumer))) {
|
||||
while ((array = WMNextHashEnumeratorItem(enumer))) {
|
||||
printf("listFamilies: got family with %d items\n", WMGetArrayItemCount(array));
|
||||
WMArrayIterator i;
|
||||
Family *fam;
|
||||
char buffer[256];
|
||||
|
||||
@@ -959,6 +959,7 @@ AC_CONFIG_FILES(
|
||||
|
||||
dnl Rust implementation of WINGs libraries
|
||||
wutil-rs/Makefile
|
||||
wings-rs/Makefile
|
||||
|
||||
dnl WINGs toolkit
|
||||
WINGs/Makefile WINGs/WINGs/Makefile WINGs/po/Makefile
|
||||
|
||||
@@ -89,6 +89,8 @@ Xephyr -screen 640x480 "$xephyr_display" &
|
||||
xephyr_pid=$!
|
||||
DISPLAY="$xephyr_display" gdb \
|
||||
--directory "$project_base" \
|
||||
--directory "$HOME/src/libX11-1.5.0/build/src" \
|
||||
--quiet \
|
||||
--fullname \
|
||||
--args "$WindowMaker" -display "$xephyr_display" --for-real "$@"
|
||||
kill $xephyr_pid
|
||||
|
||||
7
wings-rs/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "wings-rs"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
wutil-rs = { path = "../wutil-rs" }
|
||||
19
wings-rs/Makefile.am
Normal file
@@ -0,0 +1,19 @@
|
||||
AUTOMAKE_OPTIONS =
|
||||
|
||||
RUST_SOURCES = \
|
||||
src/lib.rs
|
||||
|
||||
RUST_EXTRA = \
|
||||
Cargo.lock \
|
||||
Cargo.toml
|
||||
|
||||
target/debug/libwings_rs.a: $(RUST_SOURCES) $(RUST_EXTRA)
|
||||
$(CARGO) build
|
||||
|
||||
check-local:
|
||||
$(CARGO) test
|
||||
|
||||
clean-local:
|
||||
$(CARGO) clean
|
||||
|
||||
all: target/debug/libwings_rs.a
|
||||
14
wings-rs/src/lib.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{ffi::c_void, ptr::NonNull};
|
||||
use std::ffi::c_void;
|
||||
|
||||
pub struct Array {
|
||||
items: Vec<NonNull<c_void>>,
|
||||
items: Vec<usize>,
|
||||
|
trurl marked this conversation as resolved
|
||||
destructor: Option<unsafe extern "C" fn(x: *mut c_void)>,
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ pub mod ffi {
|
||||
|
||||
use std::{
|
||||
ffi::{c_int, c_void},
|
||||
ptr::{self, NonNull},
|
||||
ptr,
|
||||
};
|
||||
|
||||
pub const NOT_FOUND: c_int = -1;
|
||||
@@ -64,7 +64,7 @@ pub mod ffi {
|
||||
let array = unsafe { &mut *array };
|
||||
if let Some(f) = array.destructor {
|
||||
for item in &mut array.items {
|
||||
unsafe { (f)(item.as_ptr()) }
|
||||
unsafe { (f)(*item as *mut c_void) }
|
||||
}
|
||||
}
|
||||
array.items.clear();
|
||||
@@ -94,10 +94,8 @@ pub mod ffi {
|
||||
if array.is_null() {
|
||||
return;
|
||||
}
|
||||
if let Some(item) = NonNull::new(item) {
|
||||
unsafe {
|
||||
(*array).items.push(item);
|
||||
}
|
||||
unsafe {
|
||||
(*array).items.push(item.addr());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,9 +112,7 @@ pub mod ffi {
|
||||
if index >= array.len() {
|
||||
return;
|
||||
}
|
||||
if let Some(item) = NonNull::new(item) {
|
||||
array.insert(index, item);
|
||||
}
|
||||
array.insert(index, item.addr());
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
@@ -141,15 +137,11 @@ pub mod ffi {
|
||||
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()
|
||||
array[index] = item.addr();
|
||||
old as *mut c_void
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
@@ -168,7 +160,7 @@ pub mod ffi {
|
||||
let old = array.items.remove(index);
|
||||
if let Some(f) = array.destructor {
|
||||
unsafe {
|
||||
(f)(old.as_ptr());
|
||||
(f)(old as *mut c_void);
|
||||
}
|
||||
}
|
||||
1
|
||||
@@ -192,8 +184,8 @@ pub mod ffi {
|
||||
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)),
|
||||
Some(f) => array.items.retain(|x| unsafe { f(*x as *const c_void, cdata) != 0 }),
|
||||
None => array.items.retain(|x| ptr::eq(*x as *mut c_void, cdata)),
|
||||
}
|
||||
(original_len - array.items.len()) as c_int
|
||||
}
|
||||
@@ -207,7 +199,7 @@ pub mod ffi {
|
||||
(&(*array))
|
||||
.items
|
||||
.get(index as usize)
|
||||
.map(|p| p.as_ptr())
|
||||
.map(|p| *p as *mut c_void)
|
||||
.unwrap_or(ptr::null_mut())
|
||||
}
|
||||
}
|
||||
@@ -226,7 +218,7 @@ pub mod ffi {
|
||||
(*array)
|
||||
.items
|
||||
.pop()
|
||||
.map(|p| p.as_ptr())
|
||||
|
trurl marked this conversation as resolved
Outdated
cross
commented
Could you use Could you use `.cast()` here instead of `as` (and similarly elsewhere)?
trurl
commented
I'm not sure I can do this. Is there a I'm not sure I can do this. Is there a `.cast()` method for converting between raw pointer and non-raw-pointer types? For example, most of the time this file uses `as`, it's casting a `usize` to some flavor of `void*`.
cross
commented
oh, hmm. No, not really, then. In that case, I'd recommend using the "provenance" based functions, though: oh, hmm. No, not really, then. In that case, I'd recommend using the "provenance" based functions, though: `.with_addr` and so forth, if that's reasonable: https://doc.rust-lang.org/std/ptr/index.html#strict-provenance
trurl
commented
I don't see how I can reasonably use I do hope to be rid of I don't see how I can reasonably use `with_addr` to retain provenance when converting values stored in the array from `usize` back to pointers. The problem lies in how this API is used by existing code: the caller may provide arbitrary integers to functions like `WMInsertInArray`. When those values are queried by C code, they may be cast back to integers or dereferenced. The knowledge of whether the value is a real address with proper provenance is only in the programmer's head. As you have suggested, I'd have to track something at runtime or split this into two different array types (one for `usize`s, one for `* mut c_void`s).
I do hope to be rid of `WMArray` sooner or later, so for now I think I'll just keep the casts as-is.
|
||||
.map(|p| p as *mut c_void)
|
||||
.unwrap_or(ptr::null_mut())
|
||||
}
|
||||
}
|
||||
@@ -246,7 +238,7 @@ pub mod ffi {
|
||||
.items
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, item)| unsafe { f(item.as_ptr(), cdata) != 0 })
|
||||
.find(|(_, item)| unsafe { f(**item as *const c_void, cdata) != 0 })
|
||||
.map(|(i, _)| i as c_int)
|
||||
.unwrap_or(NOT_FOUND)
|
||||
} else {
|
||||
@@ -254,7 +246,7 @@ pub mod ffi {
|
||||
.items
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, item)| ptr::eq(item.as_ptr(), cdata))
|
||||
.find(|(_, item)| ptr::eq(**item as *const c_void, cdata))
|
||||
.map(|(i, _)| i as c_int)
|
||||
.unwrap_or(NOT_FOUND)
|
||||
}
|
||||
@@ -269,7 +261,7 @@ pub mod ffi {
|
||||
array
|
||||
.items
|
||||
.iter()
|
||||
.filter(|x| ptr::eq(x.as_ptr(), item))
|
||||
.filter(|x| ptr::eq(**x as *const c_void, item))
|
||||
.count() as c_int
|
||||
}
|
||||
|
||||
@@ -284,13 +276,17 @@ pub mod ffi {
|
||||
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!(),
|
||||
.sort_by(|a, b| {
|
||||
let a = a as *const _ as *const c_void;
|
||||
let b = b as *const _ as *const c_void;
|
||||
match comparator(a, b).signum() {
|
||||
-1 => std::cmp::Ordering::Less,
|
||||
0 => std::cmp::Ordering::Equal,
|
||||
1 => std::cmp::Ordering::Greater,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
@@ -304,7 +300,7 @@ pub mod ffi {
|
||||
}
|
||||
unsafe {
|
||||
for a in &mut (*array).items {
|
||||
(f)(a.as_ptr(), data);
|
||||
(f)(*a as *mut c_void, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -326,7 +322,7 @@ pub mod ffi {
|
||||
unsafe {
|
||||
*iter = 0;
|
||||
}
|
||||
x.as_ptr()
|
||||
*x as *mut c_void
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,7 +344,7 @@ pub mod ffi {
|
||||
unsafe {
|
||||
*iter = (array.items.len() - 1) as c_int;
|
||||
}
|
||||
x.as_ptr()
|
||||
*x as *mut c_void
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -368,7 +364,7 @@ pub mod ffi {
|
||||
unsafe {
|
||||
*iter += 1;
|
||||
}
|
||||
i.as_ptr()
|
||||
*i as *mut c_void
|
||||
}
|
||||
None => {
|
||||
unsafe {
|
||||
@@ -394,7 +390,7 @@ pub mod ffi {
|
||||
unsafe {
|
||||
*iter -= 1;
|
||||
}
|
||||
i.as_ptr()
|
||||
*i as *mut c_void
|
||||
}
|
||||
None => {
|
||||
unsafe {
|
||||
|
||||
@@ -69,7 +69,6 @@ impl HashTable {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct StringKey(*const u8);
|
||||
|
||||
@@ -85,6 +84,12 @@ impl PartialEq for StringKey {
|
||||
|
||||
impl Eq for StringKey {}
|
||||
|
||||
impl std::fmt::Debug for StringKey {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "StringKey({:?})", unsafe { std::ffi::CStr::from_ptr(self.0) })
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for StringKey {
|
||||
fn hash<H: Hasher>(&self, h: &mut H) {
|
||||
if self.0.is_null() {
|
||||
@@ -291,4 +296,43 @@ pub mod ffi {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use std::{ffi::CString, ptr};
|
||||
|
||||
#[test]
|
||||
fn enumerate_nonempty() {
|
||||
unsafe {
|
||||
let table = WMCreateStringHashTable();
|
||||
let k1 = CString::new("hello").unwrap().into_raw();
|
||||
let v1 = CString::new("world").unwrap().into_raw();
|
||||
let k2 = CString::new("foo").unwrap().into_raw();
|
||||
let v2 = CString::new("bar").unwrap().into_raw();
|
||||
WMHashInsert(table, k1.cast(), v1.cast());
|
||||
WMHashInsert(table, k2.cast(), v2.cast());
|
||||
|
||||
let i = WMEnumerateHashTable(table);
|
||||
|
||||
let v = WMNextHashEnumeratorItem(i);
|
||||
assert_ne!(v, ptr::null_mut());
|
||||
assert!(v == v1.cast() || v == v2.cast());
|
||||
|
||||
let v = WMNextHashEnumeratorItem(i);
|
||||
assert_ne!(v, ptr::null_mut());
|
||||
assert!(v == v1.cast() || v == v2.cast());
|
||||
|
||||
let v = WMNextHashEnumeratorItem(i);
|
||||
assert_eq!(v, ptr::null_mut());
|
||||
WMFreeHashEnumerator(i);
|
||||
|
||||
let _ = CString::from_raw(k1);
|
||||
let _ = CString::from_raw(v1);
|
||||
let _ = CString::from_raw(k2);
|
||||
let _ = CString::from_raw(v2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
I had thought at one point that it might be nice to try to make this generic over some type T, and bifurcate usage between
c_voidandusize, but looking at the sheer volume of places this code is used, I think that would be tedious and premature. It's a bummer that we have to do a lot of pointer casting as a result, but ... c'est la vie.On the bright side, ditching
WMArrayandWMHashTablewill be possible soon-ish.