In this case, we can replace the red-black tree with a sorted Vec. That makes rewriting it pretty simple, although it is yet another thing that we actually want to get rid of at some point.
In this case, we can replace the red-black tree with a sorted Vec. That makes rewriting it pretty simple, although it is yet another thing that we actually want to get rid of at some point.
We should eventually get rid of this entirely, in favor of something along the
lines of a sorted Vec that is fully typed (so the payload isn't just a void*).
trurl
requested review from cross 2025-10-28 21:27:15 -04:00
It feels like you almost don't need this at all; you could do everything you need to do with the Bag abstraction by just making it a newtype over a BTreeMap, and then dereferencing the newtype pointer at the FFI boundary. Something like,
use std::collections::BTreeMap;
use std::ffi::{c_int, c_void};
use std::ptr::{self, NonNull};
pub struct Bag(BTreeMap<c_int, NonNull<c_void>>);
/// Basic constructor. Free with [`WMFreeBag`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreateTreeBag() -> *mut Bag {
let bag = Box::new(Bag(BTreeMap::new()));
Box::leak(bag)
}
/// Retrieves the value associated with `key`, or null.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetFromBag(bag: *mut Bag, key: c_int) -> *mut c_void {
unsafe { bag.as_mut() }
.and_then(|bag| bag.0.get(&key))
.map(|p| p.as_ptr())
.unwrap_or(ptr::null_mut())
}
(And so on.)
It feels like you almost don't need this at all; you could do everything you need to do with the `Bag` abstraction by just making it a newtype over a `BTreeMap`, and then dereferencing the newtype pointer at the FFI boundary. Something like,
```
use std::collections::BTreeMap;
use std::ffi::{c_int, c_void};
use std::ptr::{self, NonNull};
pub struct Bag(BTreeMap<c_int, NonNull<c_void>>);
/// Basic constructor. Free with [`WMFreeBag`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreateTreeBag() -> *mut Bag {
let bag = Box::new(Bag(BTreeMap::new()));
Box::leak(bag)
}
/// Retrieves the value associated with `key`, or null.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMGetFromBag(bag: *mut Bag, key: c_int) -> *mut c_void {
unsafe { bag.as_mut() }
.and_then(|bag| bag.0.get(&key))
.map(|p| p.as_ptr())
.unwrap_or(ptr::null_mut())
}
```
(And so on.)
Only putting functionality into the FFI functions makes sense. (I've done similarly in other refactors that you're looking at.) I was thinking to have most operations addressable via completely safe Rust, to reduce the amount of unsafe in unit tests, but for this module the additional lines of code may not be warranted.
I elected to use a sorted Vec instead of a BTreeMap because of the iterator interface: there isn't a proper double-ended BTreeMap iterator in Rust, and WUtils iterators are assumed to be allocation-free. I'm not super inclined to refactor the interface very much right now, so it seems simpler to use an implementation that allows use of an int as an iterator. Unless I'm overlooking a strong reason to use a BTreeMap internally, the sorted Vec seems fine.
Only putting functionality into the FFI functions makes sense. (I've done similarly in other refactors that you're looking at.) I was thinking to have most operations addressable via completely safe Rust, to reduce the amount of unsafe in unit tests, but for this module the additional lines of code may not be warranted.
I elected to use a sorted `Vec` instead of a `BTreeMap` because of the iterator interface: there isn't a proper double-ended `BTreeMap` iterator in Rust, and WUtils iterators are assumed to be allocation-free. I'm not super inclined to refactor the interface very much right now, so it seems simpler to use an implementation that allows use of an int as an iterator. Unless I'm overlooking a strong reason to use a `BTreeMap` internally, the sorted `Vec` seems fine.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
In this case, we can replace the red-black tree with a sorted Vec. That makes rewriting it pretty simple, although it is yet another thing that we actually want to get rid of at some point.
@@ -0,0 +15,4 @@};#[derive(Default)]pub struct Bag {It feels like you almost don't need this at all; you could do everything you need to do with the
Bagabstraction by just making it a newtype over aBTreeMap, and then dereferencing the newtype pointer at the FFI boundary. Something like,(And so on.)
Only putting functionality into the FFI functions makes sense. (I've done similarly in other refactors that you're looking at.) I was thinking to have most operations addressable via completely safe Rust, to reduce the amount of unsafe in unit tests, but for this module the additional lines of code may not be warranted.
I elected to use a sorted
Vecinstead of aBTreeMapbecause of the iterator interface: there isn't a proper double-endedBTreeMapiterator in Rust, and WUtils iterators are assumed to be allocation-free. I'm not super inclined to refactor the interface very much right now, so it seems simpler to use an implementation that allows use of an int as an iterator. Unless I'm overlooking a strong reason to use aBTreeMapinternally, the sortedVecseems fine.ok, I think that's fine.
a2940e44adto0097d1819e