Rewrite WUtils bagtree.c in Rust #4

Merged
trurl merged 4 commits from trurl/wmaker:refactor/wutil-rs-bagtree into refactor/wutil-rs 2025-12-08 13:00:34 -05:00
7 changed files with 385 additions and 803 deletions

View File

@@ -65,7 +65,6 @@ libWINGs_la_SOURCES = \
wwindow.c
libWUtil_la_SOURCES = \
bagtree.c \
error.c \
error.h \
findfile.c \

View File

@@ -173,7 +173,7 @@ typedef struct {
typedef int WMArrayIterator;
typedef void *WMBagIterator;
typedef int WMBagIterator;
typedef void WMNotificationObserverAction(void *observerData,
@@ -479,7 +479,7 @@ void* WMArrayPrevious(WMArray *array, WMArrayIterator *iter);
for (var = WMArrayLast(array, &(i)); (i) != WANotFound; \
var = WMArrayPrevious(array, &(i)))
/* ---[ WINGs/bagtree.c ]------------------------------------------------- */
/* ---[ wutil-rs/src/bag.rs ]------------------------------------------------- */
/*
* Tree bags use a red-black tree for storage.
@@ -494,58 +494,16 @@ void* WMArrayPrevious(WMArray *array, WMArrayIterator *iter);
* Slow for storing small numbers of elements
*/
#define WMCreateBag(size) WMCreateTreeBag()
#define WMCreateBagWithDestructor(size, d) WMCreateTreeBagWithDestructor(d)
WMBag* WMCreateTreeBag(void);
WMBag* WMCreateTreeBagWithDestructor(WMFreeDataProc *destructor);
int WMGetBagItemCount(WMBag *bag);
void WMAppendBag(WMBag *bag, WMBag *other);
void WMPutInBag(WMBag *bag, void *item);
/* insert will increment the index of elements after it by 1 */
void WMInsertInBag(WMBag *bag, int index, void *item);
/* erase will remove the element from the bag,
* but will keep the index of the other elements unchanged */
int WMEraseFromBag(WMBag *bag, int index);
/* delete and remove will remove the elements and cause the elements
* after them to decrement their indexes by 1 */
int WMDeleteFromBag(WMBag *bag, int index);
int WMRemoveFromBag(WMBag *bag, void *item);
void* WMGetFromBag(WMBag *bag, int index);
void* WMReplaceInBag(WMBag *bag, int index, void *item);
#define WMSetInBag(bag, index, item) WMReplaceInBag(bag, index, item)
/* comparer must return:
* < 0 if a < b
* > 0 if a > b
* = 0 if a = b
*/
void WMSortBag(WMBag *bag, WMCompareDataProc *comparer);
void WMSetInBag(WMBag *bag, int index, void *item);
void WMEmptyBag(WMBag *bag);
void WMFreeBag(WMBag *bag);
void WMMapBag(WMBag *bag, void (*function)(void*, void*), void *data);
int WMGetFirstInBag(WMBag *bag, void *item);
int WMCountInBag(WMBag *bag, void *item);
int WMFindInBag(WMBag *bag, WMMatchDataProc *match, void *cdata);
void* WMBagFirst(WMBag *bag, WMBagIterator *ptr);
void* WMBagLast(WMBag *bag, WMBagIterator *ptr);
@@ -557,16 +515,11 @@ void* WMBagPrevious(WMBag *bag, WMBagIterator *ptr);
void* WMBagIteratorAtIndex(WMBag *bag, int index, WMBagIterator *ptr);
int WMBagIndexForIterator(WMBag *bag, WMBagIterator ptr);
/* The following 2 macros assume that the bag doesn't change in the for loop */
#define WM_ITERATE_BAG(bag, var, i) \
for (var = WMBagFirst(bag, &(i)); (i) != NULL; \
var = WMBagNext(bag, &(i)))
/* The following macro assumes that the bag doesn't change in the for loop */
#define WM_ETARETI_BAG(bag, var, i) \
for (var = WMBagLast(bag, &(i)); (i) != NULL; \
for (var = WMBagLast(bag, &(i)); (i) >= 0; \
var = WMBagPrevious(bag, &(i)))

View File

@@ -1,745 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include "WUtil.h"
typedef struct W_Node {
struct W_Node *parent;
struct W_Node *left;
struct W_Node *right;
int color;
void *data;
int index;
} W_Node;
typedef struct W_Bag {
W_Node *root;
W_Node *nil; /* sentinel */
int count;
void (*destructor) (void *item);
} W_Bag;
#define IS_LEFT(node) (node == node->parent->left)
#define IS_RIGHT(node) (node == node->parent->right)
static void leftRotate(W_Bag * tree, W_Node * node)
{
W_Node *node2;
node2 = node->right;
node->right = node2->left;
node2->left->parent = node;
node2->parent = node->parent;
if (node->parent == tree->nil) {
tree->root = node2;
} else {
if (IS_LEFT(node)) {
node->parent->left = node2;
} else {
node->parent->right = node2;
}
}
node2->left = node;
node->parent = node2;
}
static void rightRotate(W_Bag * tree, W_Node * node)
{
W_Node *node2;
node2 = node->left;
node->left = node2->right;
node2->right->parent = node;
node2->parent = node->parent;
if (node->parent == tree->nil) {
tree->root = node2;
} else {
if (IS_LEFT(node)) {
node->parent->left = node2;
} else {
node->parent->right = node2;
}
}
node2->right = node;
node->parent = node2;
}
static void treeInsert(W_Bag * tree, W_Node * node)
{
W_Node *y = tree->nil;
W_Node *x = tree->root;
while (x != tree->nil) {
y = x;
if (node->index <= x->index)
x = x->left;
else
x = x->right;
}
node->parent = y;
if (y == tree->nil)
tree->root = node;
else if (node->index <= y->index)
y->left = node;
else
y->right = node;
}
static void rbTreeInsert(W_Bag * tree, W_Node * node)
{
W_Node *y;
treeInsert(tree, node);
node->color = 'R';
while (node != tree->root && node->parent->color == 'R') {
if (IS_LEFT(node->parent)) {
y = node->parent->parent->right;
if (y->color == 'R') {
node->parent->color = 'B';
y->color = 'B';
node->parent->parent->color = 'R';
node = node->parent->parent;
} else {
if (IS_RIGHT(node)) {
node = node->parent;
leftRotate(tree, node);
}
node->parent->color = 'B';
node->parent->parent->color = 'R';
rightRotate(tree, node->parent->parent);
}
} else {
y = node->parent->parent->left;
if (y->color == 'R') {
node->parent->color = 'B';
y->color = 'B';
node->parent->parent->color = 'R';
node = node->parent->parent;
} else {
if (IS_LEFT(node)) {
node = node->parent;
rightRotate(tree, node);
}
node->parent->color = 'B';
node->parent->parent->color = 'R';
leftRotate(tree, node->parent->parent);
}
}
}
tree->root->color = 'B';
}
static void rbDeleteFixup(W_Bag * tree, W_Node * node)
{
W_Node *w;
while (node != tree->root && node->color == 'B') {
if (IS_LEFT(node)) {
w = node->parent->right;
if (w->color == 'R') {
w->color = 'B';
node->parent->color = 'R';
leftRotate(tree, node->parent);
w = node->parent->right;
}
if (w->left->color == 'B' && w->right->color == 'B') {
w->color = 'R';
node = node->parent;
} else {
if (w->right->color == 'B') {
w->left->color = 'B';
w->color = 'R';
rightRotate(tree, w);
w = node->parent->right;
}
w->color = node->parent->color;
node->parent->color = 'B';
w->right->color = 'B';
leftRotate(tree, node->parent);
node = tree->root;
}
} else {
w = node->parent->left;
if (w->color == 'R') {
w->color = 'B';
node->parent->color = 'R';
rightRotate(tree, node->parent);
w = node->parent->left;
}
if (w->left->color == 'B' && w->right->color == 'B') {
w->color = 'R';
node = node->parent;
} else {
if (w->left->color == 'B') {
w->right->color = 'B';
w->color = 'R';
leftRotate(tree, w);
w = node->parent->left;
}
w->color = node->parent->color;
node->parent->color = 'B';
w->left->color = 'B';
rightRotate(tree, node->parent);
node = tree->root;
}
}
}
node->color = 'B';
}
static W_Node *treeMinimum(W_Node * node, W_Node * nil)
{
while (node->left != nil)
node = node->left;
return node;
}
static W_Node *treeMaximum(W_Node * node, W_Node * nil)
{
while (node->right != nil)
node = node->right;
return node;
}
static W_Node *treeSuccessor(W_Node * node, W_Node * nil)
{
W_Node *y;
if (node->right != nil) {
return treeMinimum(node->right, nil);
}
y = node->parent;
while (y != nil && node == y->right) {
node = y;
y = y->parent;
}
return y;
}
static W_Node *treePredecessor(W_Node * node, W_Node * nil)
{
W_Node *y;
if (node->left != nil) {
return treeMaximum(node->left, nil);
}
y = node->parent;
while (y != nil && node == y->left) {
node = y;
y = y->parent;
}
return y;
}
static W_Node *rbTreeDelete(W_Bag * tree, W_Node * node)
{
W_Node *nil = tree->nil;
W_Node *x, *y;
if (node->left == nil || node->right == nil) {
y = node;
} else {
y = treeSuccessor(node, nil);
}
if (y->left != nil) {
x = y->left;
} else {
x = y->right;
}
x->parent = y->parent;
if (y->parent == nil) {
tree->root = x;
} else {
if (IS_LEFT(y)) {
y->parent->left = x;
} else {
y->parent->right = x;
}
}
if (y != node) {
node->index = y->index;
node->data = y->data;
}
if (y->color == 'B') {
rbDeleteFixup(tree, x);
}
return y;
}
static W_Node *treeSearch(W_Node * root, W_Node * nil, int index)
{
if (root == nil || root->index == index) {
return root;
}
if (index < root->index) {
return treeSearch(root->left, nil, index);
} else {
return treeSearch(root->right, nil, index);
}
}
static W_Node *treeFind(W_Node * root, W_Node * nil, void *data)
{
W_Node *tmp;
if (root == nil || root->data == data)
return root;
tmp = treeFind(root->left, nil, data);
if (tmp != nil)
return tmp;
tmp = treeFind(root->right, nil, data);
return tmp;
}
#if 0
static char buf[512];
static void printNodes(W_Node * node, W_Node * nil, int depth)
{
if (node == nil) {
return;
}
printNodes(node->left, nil, depth + 1);
memset(buf, ' ', depth * 2);
buf[depth * 2] = 0;
if (IS_LEFT(node))
printf("%s/(%2i\n", buf, node->index);
else
printf("%s\\(%2i\n", buf, node->index);
printNodes(node->right, nil, depth + 1);
}
void PrintTree(WMBag * bag)
{
W_TreeBag *tree = (W_TreeBag *) bag->data;
printNodes(tree->root, tree->nil, 0);
}
#endif
WMBag *WMCreateTreeBag(void)
{
return WMCreateTreeBagWithDestructor(NULL);
}
WMBag *WMCreateTreeBagWithDestructor(WMFreeDataProc * destructor)
{
WMBag *bag;
bag = wmalloc(sizeof(WMBag));
bag->nil = wmalloc(sizeof(W_Node));
bag->nil->left = bag->nil->right = bag->nil->parent = bag->nil;
bag->nil->index = WBNotFound;
bag->root = bag->nil;
bag->destructor = destructor;
return bag;
}
int WMGetBagItemCount(WMBag * self)
{
return self->count;
}
void WMAppendBag(WMBag * self, WMBag * bag)
{
WMBagIterator ptr;
void *data;
for (data = WMBagFirst(bag, &ptr); data != NULL; data = WMBagNext(bag, &ptr)) {
WMPutInBag(self, data);
}
}
void WMPutInBag(WMBag * self, void *item)
{
W_Node *ptr;
ptr = wmalloc(sizeof(W_Node));
ptr->data = item;
ptr->index = self->count;
ptr->left = self->nil;
ptr->right = self->nil;
ptr->parent = self->nil;
rbTreeInsert(self, ptr);
self->count++;
}
void WMInsertInBag(WMBag * self, int index, void *item)
{
W_Node *ptr;
ptr = wmalloc(sizeof(W_Node));
ptr->data = item;
ptr->index = index;
ptr->left = self->nil;
ptr->right = self->nil;
ptr->parent = self->nil;
rbTreeInsert(self, ptr);
while ((ptr = treeSuccessor(ptr, self->nil)) != self->nil) {
ptr->index++;
}
self->count++;
}
static int treeDeleteNode(WMBag * self, W_Node *ptr)
{
if (ptr != self->nil) {
W_Node *tmp;
self->count--;
tmp = treeSuccessor(ptr, self->nil);
while (tmp != self->nil) {
tmp->index--;
tmp = treeSuccessor(tmp, self->nil);
}
ptr = rbTreeDelete(self, ptr);
if (self->destructor)
self->destructor(ptr->data);
wfree(ptr);
return 1;
}
return 0;
}
int WMRemoveFromBag(WMBag * self, void *item)
{
W_Node *ptr = treeFind(self->root, self->nil, item);
return treeDeleteNode(self, ptr);
}
int WMEraseFromBag(WMBag * self, int index)
{
W_Node *ptr = treeSearch(self->root, self->nil, index);
if (ptr != self->nil) {
self->count--;
ptr = rbTreeDelete(self, ptr);
if (self->destructor)
self->destructor(ptr->data);
wfree(ptr);
wassertrv(self->count == 0 || self->root->index >= 0, 1);
return 1;
} else {
return 0;
}
}
int WMDeleteFromBag(WMBag * self, int index)
{
W_Node *ptr = treeSearch(self->root, self->nil, index);
return treeDeleteNode(self, ptr);
}
void *WMGetFromBag(WMBag * self, int index)
{
W_Node *node;
node = treeSearch(self->root, self->nil, index);
if (node != self->nil)
return node->data;
else
return NULL;
}
int WMGetFirstInBag(WMBag * self, void *item)
{
W_Node *node;
node = treeFind(self->root, self->nil, item);
if (node != self->nil)
return node->index;
else
return WBNotFound;
}
static int treeCount(W_Node * root, W_Node * nil, void *item)
{
int count = 0;
if (root == nil)
return 0;
if (root->data == item)
count++;
if (root->left != nil)
count += treeCount(root->left, nil, item);
if (root->right != nil)
count += treeCount(root->right, nil, item);
return count;
}
int WMCountInBag(WMBag * self, void *item)
{
return treeCount(self->root, self->nil, item);
}
void *WMReplaceInBag(WMBag * self, int index, void *item)
{
W_Node *ptr = treeSearch(self->root, self->nil, index);
void *old = NULL;
if (item == NULL) {
self->count--;
ptr = rbTreeDelete(self, ptr);
if (self->destructor)
self->destructor(ptr->data);
wfree(ptr);
} else if (ptr != self->nil) {
old = ptr->data;
ptr->data = item;
} else {
W_Node *ptr;
ptr = wmalloc(sizeof(W_Node));
ptr->data = item;
ptr->index = index;
ptr->left = self->nil;
ptr->right = self->nil;
ptr->parent = self->nil;
rbTreeInsert(self, ptr);
self->count++;
}
return old;
}
void WMSortBag(WMBag * self, WMCompareDataProc * comparer)
{
void **items;
W_Node *tmp;
int i;
if (self->count == 0)
return;
items = wmalloc(sizeof(void *) * self->count);
i = 0;
tmp = treeMinimum(self->root, self->nil);
while (tmp != self->nil) {
items[i++] = tmp->data;
tmp = treeSuccessor(tmp, self->nil);
}
qsort(&items[0], self->count, sizeof(void *), comparer);
i = 0;
tmp = treeMinimum(self->root, self->nil);
while (tmp != self->nil) {
tmp->index = i;
tmp->data = items[i++];
tmp = treeSuccessor(tmp, self->nil);
}
wfree(items);
}
static void deleteTree(WMBag * self, W_Node * node)
{
if (node == self->nil)
return;
deleteTree(self, node->left);
if (self->destructor)
self->destructor(node->data);
deleteTree(self, node->right);
wfree(node);
}
void WMEmptyBag(WMBag * self)
{
deleteTree(self, self->root);
self->root = self->nil;
self->count = 0;
}
void WMFreeBag(WMBag * self)
{
WMEmptyBag(self);
wfree(self->nil);
wfree(self);
}
static void mapTree(W_Bag * tree, W_Node * node, void (*function) (void *, void *), void *data)
{
if (node == tree->nil)
return;
mapTree(tree, node->left, function, data);
(*function) (node->data, data);
mapTree(tree, node->right, function, data);
}
void WMMapBag(WMBag * self, void (*function) (void *, void *), void *data)
{
mapTree(self, self->root, function, data);
}
static int findInTree(W_Bag * tree, W_Node * node, WMMatchDataProc * function, void *cdata)
{
int index;
if (node == tree->nil)
return WBNotFound;
index = findInTree(tree, node->left, function, cdata);
if (index != WBNotFound)
return index;
if ((*function) (node->data, cdata)) {
return node->index;
}
return findInTree(tree, node->right, function, cdata);
}
int WMFindInBag(WMBag * self, WMMatchDataProc * match, void *cdata)
{
return findInTree(self, self->root, match, cdata);
}
void *WMBagFirst(WMBag * self, WMBagIterator * ptr)
{
W_Node *node;
node = treeMinimum(self->root, self->nil);
if (node == self->nil) {
*ptr = NULL;
return NULL;
} else {
*ptr = node;
return node->data;
}
}
void *WMBagLast(WMBag * self, WMBagIterator * ptr)
{
W_Node *node;
node = treeMaximum(self->root, self->nil);
if (node == self->nil) {
*ptr = NULL;
return NULL;
} else {
*ptr = node;
return node->data;
}
}
void *WMBagNext(WMBag * self, WMBagIterator * ptr)
{
W_Node *node;
if (*ptr == NULL)
return NULL;
node = treeSuccessor(*ptr, self->nil);
if (node == self->nil) {
*ptr = NULL;
return NULL;
} else {
*ptr = node;
return node->data;
}
}
void *WMBagPrevious(WMBag * self, WMBagIterator * ptr)
{
W_Node *node;
if (*ptr == NULL)
return NULL;
node = treePredecessor(*ptr, self->nil);
if (node == self->nil) {
*ptr = NULL;
return NULL;
} else {
*ptr = node;
return node->data;
}
}
void *WMBagIteratorAtIndex(WMBag * self, int index, WMBagIterator * ptr)
{
W_Node *node;
node = treeSearch(self->root, self->nil, index);
if (node == self->nil) {
*ptr = NULL;
return NULL;
} else {
*ptr = node;
return node->data;
}
}
int WMBagIndexForIterator(WMBag * bag, WMBagIterator ptr)
{
/* Parameter not used, but tell the compiler that it is ok */
(void) bag;
return ((W_Node *) ptr)->index;
}

View File

@@ -109,16 +109,16 @@ void Shutdown(WShutdownMode mode)
}
}
static void restoreWindows(WMBag * bag, WMBagIterator iter)
static void restoreWindows(WMBag * bag, WMBagIterator *iter)
{
WCoreWindow *next;
WCoreWindow *core;
WWindow *wwin;
if (iter == NULL) {
core = WMBagFirst(bag, &iter);
if (*iter < 0) {
core = WMBagFirst(bag, iter);
} else {
core = WMBagNext(bag, &iter);
core = WMBagNext(bag, iter);
}
if (core == NULL)
@@ -168,7 +168,8 @@ void RestoreDesktop(WScreen * scr)
wDestroyInspectorPanels();
/* reparent windows back to the root window, keeping the stacking order */
restoreWindows(scr->stacking_list, NULL);
WMBagIterator iter = -1;
restoreWindows(scr->stacking_list, &iter);
XUngrabServer(dpy);
XSetInputFocus(dpy, PointerRoot, RevertToParent, CurrentTime);

View File

@@ -2,6 +2,7 @@ AUTOMAKE_OPTIONS =
RUST_SOURCES = \
src/array.rs \
src/bag.rs \
src/data.rs \
src/defines.c \
src/defines.rs \

372
wutil-rs/src/bag.rs Normal file
View File

@@ -0,0 +1,372 @@
//! Simple sorted set.
//!
//! ## Rust rewrite notes
//!
//! This was originally a full-blown red-black tree, but it was really only used
//! to keep a sorted list. It has been rewritten as a sorted `Vec`. We
//! technically no longer have O(log(n)) insertion time. Given set sizes and
//! actual execution time, that shouldn't matter.
//!
//! Prefer a proper Rust collection over this for new code.
use std::{
ffi::{c_int, c_void},
ptr::NonNull,
};
#[derive(Default)]
pub struct Bag {
Outdated
Review

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.)
Outdated
Review

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.
inner: Vec<(c_int, NonNull<c_void>)>,
}
/// Sentinel iterator value. In C, just check for negative values.
pub const NOT_FOUND: c_int = -1;
impl Bag {
/// Returns `Ok(index)` if an item keyed by `key` is in `self`, else
/// `Err(index)` if an item keyed by `key` could be inserted at `index`.
fn search(&self, key: c_int) -> Result<usize, usize> {
self.inner.binary_search_by_key(&key, |(key, _)| *key)
}
/// Sets a value associated with `key` to `value`. Clobbers any extant value.
fn set(&mut self, key: c_int, value: NonNull<c_void>) {
match self.search(key) {
Ok(i) => self.inner[i] = (key, value),
Err(i) => self.inner.insert(i, (key, value)),
}
}
}
pub mod ffi {
use super::{Bag, NOT_FOUND};
use std::{
ffi::{c_int, c_void},
ptr::{self, NonNull},
};
/// Basic constructor. Free with [`WMFreeBag`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMCreateTreeBag() -> *mut Bag {
Box::leak(Box::new(Bag::default()))
}
/// 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 {
if bag.is_null() {
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
bag
.search(key)
.ok()
.and_then(|index| bag.inner.get(index).copied())
.map(|(_, value)| value.as_ptr())
.unwrap_or(ptr::null_mut())
}
/// Sets the value associated with `key` to `item`. If `item` is null,
/// removes any extant value.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMSetInBag(bag: *mut Bag, key: c_int, item: *mut c_void) {
if bag.is_null() {
return;
}
let bag = unsafe { &mut *bag };
if let Some(item) = NonNull::new(item) {
bag.set(key, item);
} else {
if let Some(i) = bag.search(key).ok() {
bag.inner.remove(i);
}
}
}
/// Clears the contents of `bag`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMEmptyBag(bag: *mut Bag) {
if bag.is_null() {
return;
}
unsafe {
(*bag).inner.clear();
}
}
/// Deletes `bag`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMFreeBag(bag: *mut Bag) {
if bag.is_null() {
return;
}
unsafe {
let _ = Box::from_raw(bag);
}
}
/// Initializes `ptr` to the first element of `bag` and returns that
/// element. Sets `ptr` to [`NOT_FOUND`] and returns null if `bag` is empty.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMBagFirst(bag: *mut Bag, ptr: *mut c_int) -> *mut c_void {
if ptr.is_null() {
return ptr::null_mut();
}
let ptr = unsafe { &mut *ptr };
if bag.is_null() {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
if let Some((_, value)) = bag.inner.first() {
*ptr = 0;
value.as_ptr()
} else {
*ptr = NOT_FOUND;
ptr::null_mut()
}
}
/// Initializes `ptr` to the last element of `bag` and returns that
/// element. Sets `ptr` to [`NOT_FOUND`] and returns null if `bag` is empty.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMBagLast(bag: *mut Bag, ptr: *mut c_int) -> *mut c_void {
if ptr.is_null() {
return ptr::null_mut();
}
let ptr = unsafe { &mut *ptr };
if bag.is_null() {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
if let Some((_, value)) = bag.inner.last() {
*ptr = (bag.inner.len() - 1) as c_int;
value.as_ptr()
} else {
*ptr = NOT_FOUND;
ptr::null_mut()
}
}
/// Advances `ptr` to the next element of `bag` and returns it. Sets `ptr`
/// to [`NOT_FOUND`] and returns null if `ptr` is already at the end of
/// `bag`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMBagNext(bag: *mut Bag, ptr: *mut c_int) -> *mut c_void {
if ptr.is_null() {
return ptr::null_mut();
}
let ptr = unsafe { &mut *ptr };
if bag.is_null() {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
if *ptr < 0 {
return ptr::null_mut();
}
*ptr += 1;
if let Some((_, value)) = bag.inner.get(*ptr as usize) {
value.as_ptr()
} else {
*ptr = NOT_FOUND;
ptr::null_mut()
}
}
/// Decrements `ptr` to the previous element of `bag` and returns it. Sets `ptr`
/// to [`NOT_FOUND`] and returns null if `ptr` is already at the beginning of
/// `bag`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMBagPrevious(bag: *mut Bag, ptr: *mut c_int) -> *mut c_void {
if ptr.is_null() {
return ptr::null_mut();
}
let ptr = unsafe { &mut *ptr };
if bag.is_null() {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
if *ptr <= 0 {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
*ptr -= 1;
if let Some((_, value)) = bag.inner.get(*ptr as usize) {
value.as_ptr()
} else {
*ptr = NOT_FOUND;
ptr::null_mut()
}
}
/// Sets `ptr` to the element of `bag` with `key` and returns the associated
/// value. Sets `ptr` to [`NOT_FOUND`] and returns null if there is no such
/// element.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WMBagIteratorAtIndex(
bag: *mut Bag,
key: c_int,
ptr: *mut c_int,
) -> *mut c_void {
if ptr.is_null() {
return ptr::null_mut();
}
let ptr = unsafe { &mut *ptr };
if bag.is_null() {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
let bag = unsafe { &mut *bag };
if key < 0 {
*ptr = NOT_FOUND;
return ptr::null_mut();
}
if let Some(index) = bag.search(key).ok() {
*ptr = index as c_int;
bag.inner[index].1.as_ptr()
} else {
*ptr = NOT_FOUND;
ptr::null_mut()
}
}
}
#[cfg(test)]
mod test {
use super::{ffi, Bag, NOT_FOUND};
use std::{
ffi::{c_int, c_void},
ptr::NonNull,
};
fn void_of<T>(t: &mut T) -> NonNull<c_void> {
NonNull::new(t as *mut _ as *mut c_void).unwrap()
}
#[test]
fn insert_out_of_order() {
let mut b = Bag::default();
let mut x = 3u16;
let mut y = 4u16;
let mut z = 5u16;
b.set(7, void_of(&mut z));
b.set(3, void_of(&mut y));
b.set(5, void_of(&mut x));
assert_eq!(
b.inner,
&[
(3, void_of(&mut y)),
(5, void_of(&mut x)),
(7, void_of(&mut z))
]
);
}
#[test]
fn iterate_forward() {
let mut b = Bag::default();
let mut x = 3u16;
let mut y = 4u16;
let mut z = 5u16;
b.set(7, void_of(&mut z));
b.set(3, void_of(&mut y));
b.set(5, void_of(&mut x));
let mut iterator: c_int = NOT_FOUND;
unsafe {
assert_eq!(
ffi::WMBagFirst(&mut b, &mut iterator),
void_of(&mut y).as_ptr()
);
assert_eq!(iterator, 0);
assert_eq!(
ffi::WMBagNext(&mut b, &mut iterator),
void_of(&mut x).as_ptr()
);
assert_eq!(iterator, 1);
assert_eq!(
ffi::WMBagNext(&mut b, &mut iterator),
void_of(&mut z).as_ptr()
);
assert_eq!(iterator, 2);
assert!(ffi::WMBagNext(&mut b, &mut iterator).is_null());
assert_eq!(iterator, NOT_FOUND);
}
}
#[test]
fn iterate_backward() {
let mut b = Bag::default();
let mut x = 3u16;
let mut y = 4u16;
let mut z = 5u16;
b.set(7, void_of(&mut z));
b.set(3, void_of(&mut y));
b.set(5, void_of(&mut x));
let mut iterator: c_int = NOT_FOUND;
unsafe {
assert_eq!(
ffi::WMBagLast(&mut b, &mut iterator),
void_of(&mut z).as_ptr()
);
assert_eq!(iterator, 2);
assert_eq!(
ffi::WMBagPrevious(&mut b, &mut iterator),
void_of(&mut x).as_ptr()
);
assert_eq!(iterator, 1);
assert_eq!(
ffi::WMBagPrevious(&mut b, &mut iterator),
void_of(&mut y).as_ptr()
);
assert_eq!(iterator, 0);
assert!(ffi::WMBagPrevious(&mut b, &mut iterator).is_null());
assert_eq!(iterator, NOT_FOUND);
}
}
#[test]
fn find_iterate() {
let mut b = Bag::default();
let mut x = 3u16;
let mut y = 4u16;
let mut z = 5u16;
b.set(7, void_of(&mut z));
b.set(3, void_of(&mut y));
b.set(5, void_of(&mut x));
let mut iterator: c_int = NOT_FOUND;
unsafe {
assert_eq!(
ffi::WMBagIteratorAtIndex(&mut b, 5, &mut iterator),
void_of(&mut x).as_ptr()
);
assert_eq!(iterator, 1);
assert_eq!(
ffi::WMBagNext(&mut b, &mut iterator),
void_of(&mut z).as_ptr()
);
assert_eq!(iterator, 2);
}
}
}

View File

@@ -1,4 +1,5 @@
pub mod array;
pub mod bag;
pub mod data;
pub mod defines;
pub mod find_file;