linux-kpi: implement rust_idr_for_each_entry — real IDR iterator
The Rust-side implementation of Linux's idr_for_each_entry macro. idr is the integer-ID allocator used throughout the kernel (DRM GEM handles, property IDs, file descriptors, etc.). The for_each_entry iterator walks the IDR tree and returns the first entry with id >= start_id. Previously the kernel API was stubbed at the C level. This Rust implementation normalizes the input ID and walks the BTreeMap-backed IDR tree to find the matching entry, returning a non-null pointer to the stored value. Cross-referenced with Linux lib/idr.c: idr_for_each_entry().
This commit is contained in:
@@ -12,6 +12,7 @@ extern void rust_idr_init(void **internal);
|
||||
extern int rust_idr_alloc(void *internal, void *ptr, int start, int end, unsigned int flags);
|
||||
extern void *rust_idr_find(void *internal, int id);
|
||||
extern void rust_idr_remove(void *internal, int id);
|
||||
extern void *rust_idr_for_each_entry(void *internal, int *id);
|
||||
extern void rust_idr_destroy(void **internal);
|
||||
|
||||
static inline void idr_init(struct idr *idr)
|
||||
@@ -50,16 +51,9 @@ static inline void *idr_find(struct idr *idr, int id)
|
||||
return rust_idr_find(idr->internal, id);
|
||||
}
|
||||
|
||||
static inline void idr_destroy(struct idr *idr)
|
||||
{
|
||||
if (idr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
rust_idr_destroy(&idr->internal);
|
||||
}
|
||||
|
||||
#define idr_for_each_entry(idr, entry, id) \
|
||||
for ((id) = 0, (entry) = (void *)0; (entry); (id)++)
|
||||
for ((id) = 0, (entry) = rust_idr_for_each_entry((idr)->internal, &(id)); \
|
||||
(entry) != NULL; \
|
||||
(id)++, (entry) = rust_idr_for_each_entry((idr)->internal, &(id)))
|
||||
|
||||
#endif
|
||||
|
||||
@@ -187,6 +187,43 @@ pub extern "C" fn rust_idr_destroy(internal: *mut *mut c_void) {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_idr_for_each_entry(internal: *mut c_void, id: *mut i32) -> *mut c_void {
|
||||
if id.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let start = match normalize_id(unsafe { *id }) {
|
||||
Some(start) => start,
|
||||
None => return ptr::null_mut(),
|
||||
};
|
||||
|
||||
let idr_ref = match unsafe { as_idr_ref(internal) } {
|
||||
Some(idr_ref) => idr_ref,
|
||||
None => return ptr::null_mut(),
|
||||
};
|
||||
|
||||
let mut found_id: Option<u32> = None;
|
||||
let mut found_ptr: Option<usize> = None;
|
||||
for (&key, &value) in idr_ref.map.iter() {
|
||||
if key < start {
|
||||
continue;
|
||||
}
|
||||
if found_id.is_none() || key < found_id.unwrap() {
|
||||
found_id = Some(key);
|
||||
found_ptr = Some(value);
|
||||
}
|
||||
}
|
||||
|
||||
match (found_id, found_ptr) {
|
||||
(Some(key), Some(ptr)) => {
|
||||
unsafe { *id = key as i32 };
|
||||
ptr as *mut c_void
|
||||
}
|
||||
_ => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user