diff --git a/WINGs/Makefile.am b/WINGs/Makefile.am index 4b2f4566..74b3ffea 100644 --- a/WINGs/Makefile.am +++ b/WINGs/Makefile.am @@ -65,7 +65,6 @@ libWINGs_la_SOURCES = \ wwindow.c libWUtil_la_SOURCES = \ - bagtree.c \ error.c \ error.h \ findfile.c \ diff --git a/WINGs/WINGs/WUtil.h b/WINGs/WINGs/WUtil.h index 2797429c..060c1adb 100644 --- a/WINGs/WINGs/WUtil.h +++ b/WINGs/WINGs/WUtil.h @@ -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))) diff --git a/WINGs/bagtree.c b/WINGs/bagtree.c deleted file mode 100644 index 4b3062ca..00000000 --- a/WINGs/bagtree.c +++ /dev/null @@ -1,745 +0,0 @@ - -#include -#include - -#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; -} diff --git a/src/shutdown.c b/src/shutdown.c index 300a2692..89ac34e9 100644 --- a/src/shutdown.c +++ b/src/shutdown.c @@ -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); diff --git a/wutil-rs/Makefile.am b/wutil-rs/Makefile.am index a8dab76f..e2ee2d06 100644 --- a/wutil-rs/Makefile.am +++ b/wutil-rs/Makefile.am @@ -2,6 +2,7 @@ AUTOMAKE_OPTIONS = RUST_SOURCES = \ src/array.rs \ + src/bag.rs \ src/data.rs \ src/defines.c \ src/defines.rs \ diff --git a/wutil-rs/src/bag.rs b/wutil-rs/src/bag.rs new file mode 100644 index 00000000..ffca050d --- /dev/null +++ b/wutil-rs/src/bag.rs @@ -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 { + inner: Vec<(c_int, NonNull)>, +} + +/// 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 { + 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) { + 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: &mut T) -> NonNull { + 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); + } + } +} diff --git a/wutil-rs/src/lib.rs b/wutil-rs/src/lib.rs index c2315e11..ab94d66d 100644 --- a/wutil-rs/src/lib.rs +++ b/wutil-rs/src/lib.rs @@ -1,4 +1,5 @@ pub mod array; +pub mod bag; pub mod data; pub mod defines; pub mod find_file;