From 27cbc20355086938a96f0fc24639ae21b3020b18 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 27 Oct 2022 00:00:29 +0300 Subject: [PATCH] util/addon: use AVL trees --- include/wlr/util/addon.h | 13 +- util/addon.c | 261 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 255 insertions(+), 19 deletions(-) diff --git a/include/wlr/util/addon.h b/include/wlr/util/addon.h index c64200cf9..62834c479 100644 --- a/include/wlr/util/addon.h +++ b/include/wlr/util/addon.h @@ -9,26 +9,29 @@ #ifndef WLR_UTIL_ADDON_H #define WLR_UTIL_ADDON_H -#include +#include struct wlr_addon_set { // private state - struct wl_list addons; + struct wlr_addon *root; }; struct wlr_addon; struct wlr_addon_interface { const char *name; - // Has to call wlr_addon_finish() void (*destroy)(struct wlr_addon *addon); }; struct wlr_addon { - const struct wlr_addon_interface *impl; // private state + struct wlr_addon_set *set; const void *owner; - struct wl_list link; + const struct wlr_addon_interface *impl; + struct wlr_addon *parent; // NULL if it's set->root + struct wlr_addon *left; + struct wlr_addon *right; + int8_t balance; }; void wlr_addon_set_init(struct wlr_addon_set *set); diff --git a/util/addon.c b/util/addon.c index 10aed744a..5976b799a 100644 --- a/util/addon.c +++ b/util/addon.c @@ -1,18 +1,119 @@ #include +#include #include #include -#include #include +static ptrdiff_t compare(struct wlr_addon *addon, const void *owner, + const struct wlr_addon_interface *impl) { + ptrdiff_t owner_diff = (ptrdiff_t)addon->owner - (ptrdiff_t)owner; + if (owner_diff != 0) { + return owner_diff; + } + return (ptrdiff_t)addon->impl - (ptrdiff_t)impl; +} + +// A (addon (child)) => A (child) +static void replace(struct wlr_addon *addon, struct wlr_addon *child) { + if (addon->parent == NULL) { + addon->set->root = child; + } else if (addon->parent->left == addon) { + addon->parent->left = child; + } else { // addon->parent->right == addon + addon->parent->right = child; + } + if (child != NULL) { + child->parent = addon->parent; + } +} + +// parent (A, addon (B, C)) => addon (parent (A, B), C) +static struct wlr_addon *rotate_left(struct wlr_addon *parent, + struct wlr_addon *addon) { + parent->right = addon->left; + if (addon->left != NULL) { + addon->left->parent = parent; + } + addon->left = parent; + parent->parent = addon; + if (addon->balance == 0) { + addon->balance = -1; + parent->balance = 1; + } else { + addon->balance = 0; + parent->balance = 0; + } + return addon; +} + +// parent (addon (A, B), C) => addon (A, parent (B, C)) +static struct wlr_addon *rotate_right(struct wlr_addon *parent, + struct wlr_addon *addon) { + parent->left = addon->right; + if (addon->right != NULL) { + addon->right->parent = parent; + } + addon->right = parent; + parent->parent = addon; + if (addon->balance == 0) { + addon->balance = -1; + parent->balance = 1; + } else { + addon->balance = 0; + parent->balance = 0; + } + return addon; +} + +// parent (addon (A, mid (B, C)), D) => mid (addon (A, B), parent (C, D)) +static struct wlr_addon *rotate_leftright(struct wlr_addon *parent, + struct wlr_addon *addon) { + struct wlr_addon *mid = addon->right; + addon->right = mid->left; + if (mid->left != NULL) { + mid->left->parent = addon; + } + mid->left = addon; + addon->parent = mid; + parent->left = mid->right; + if (mid->right != NULL) { + mid->right->parent = parent; + } + mid->right = parent; + parent->parent = mid; + parent->balance = mid->balance > 0 ? -1 : 0; + addon->balance = mid->balance < 0 ? 1 : 0; + return mid; +} + +// parent (A, addon (mid (B, C), D)) => mid (parent (A, B), addon (C, D)) +static struct wlr_addon *rotate_rightleft(struct wlr_addon *parent, + struct wlr_addon *addon) { + struct wlr_addon *mid = addon->left; + addon->left = mid->right; + if (mid->right != NULL) { + mid->right->parent = addon; + } + mid->right = addon; + addon->parent = mid; + parent->right = mid->left; + if (mid->left != NULL) { + mid->left->parent = parent; + } + mid->left = parent; + parent->parent = mid; + parent->balance = mid->balance > 0 ? -1 : 0; + addon->balance = mid->balance < 0 ? 1 : 0; + return mid; +} + void wlr_addon_set_init(struct wlr_addon_set *set) { memset(set, 0, sizeof(*set)); - wl_list_init(&set->addons); } void wlr_addon_set_finish(struct wlr_addon_set *set) { - struct wlr_addon *addon, *tmp; - wl_list_for_each_safe(addon, tmp, &set->addons, link) { - addon->impl->destroy(addon); + while (set->root != NULL) { + set->root->impl->destroy(set->root); } } @@ -20,26 +121,158 @@ void wlr_addon_init(struct wlr_addon *addon, struct wlr_addon_set *set, const void *owner, const struct wlr_addon_interface *impl) { assert(owner && impl); memset(addon, 0, sizeof(*addon)); - struct wlr_addon *iter; - wl_list_for_each(iter, &set->addons, link) { - if (iter->owner == addon->owner && iter->impl == addon->impl) { - assert(0 && "Can't have two addons of the same type with the same owner"); + + struct wlr_addon *parent = NULL; + struct wlr_addon **ptr = &set->root; + while (*ptr != NULL) { + parent = *ptr; + ptrdiff_t diff = compare(*ptr, owner, impl); + if (diff < 0) { + ptr = &(*ptr)->left; + } else if (diff > 0) { + ptr = &(*ptr)->right; + } else { + assert(0 && "Can't have two addons with the same owner and impl"); + return; } } - wl_list_insert(&set->addons, &addon->link); + + addon->set = set; addon->owner = owner; addon->impl = impl; + addon->parent = parent; + *ptr = addon; + + // Rebalance + for (; parent != NULL; parent = parent->parent) { + struct wlr_addon *grandparent = parent->parent; + if (parent->left == addon) { + if (parent->balance > 0) { + parent->balance = 0; + break; + } else if (parent->balance == 0) { + parent->balance = -1; + addon = parent; + continue; + } + if (addon->balance > 0) { + addon = rotate_leftright(parent, addon); + } else { + addon = rotate_right(parent, addon); + } + } else { // parent->right == addon + if (parent->balance < 0) { + parent->balance = 0; + break; + } else if (parent->balance == 0) { + parent->balance = 1; + addon = parent; + continue; + } + if (addon->balance < 0) { + addon = rotate_rightleft(parent, addon); + } else { + addon = rotate_left(parent, addon); + } + } + // Here, addon is the root of the rotated subtree + addon->parent = grandparent; + if (grandparent != NULL) { + if (grandparent->left == parent) { + grandparent->left = addon; + } else { // grandparent->right == parent + grandparent->right = addon; + } + } else { + set->root = addon; + } + break; + } } void wlr_addon_finish(struct wlr_addon *addon) { - wl_list_remove(&addon->link); + if (addon->left == NULL) { + replace(addon, addon->right); + } else if (addon->right == NULL) { + replace(addon, addon->left); + } else { + struct wlr_addon *successor = addon->right; + while (successor->left != NULL) { + successor = successor->left; + } + if (successor->parent != addon) { + replace(successor, successor->right); + successor->right = addon->right; + successor->right->parent = successor; + } + replace(addon, successor); + successor->left = addon->left; + successor->left->parent = successor; + } + + // Rebalance + for (struct wlr_addon *parent = addon->parent; + parent != NULL; parent = parent->parent) { + int balance = 0; + struct wlr_addon *grandparent = parent->parent; + if (parent->left == addon) { + if (parent->balance < 0) { + parent->balance = 0; + addon = parent; + continue; + } else if (parent->balance == 0) { + parent->balance = 1; + break; + } + balance = parent->right->balance; + if (balance < 0) { + addon = rotate_rightleft(parent, addon); + } else { + addon = rotate_left(parent, addon); + } + } else { // parent->right == addon + if (parent->balance < 0) { + parent->balance = 0; + addon = parent; + continue; + } else if (parent->balance == 0) { + parent->balance = -1; + break; + } + balance = parent->left->balance; + if (balance > 0) { + addon = rotate_leftright(parent, addon); + } else { + addon = rotate_right(parent, addon); + } + } + // Here, addon is the root of the rotated subtree + addon->parent = grandparent; + if (grandparent != NULL) { + if (grandparent->left == parent) { + grandparent->left = addon; + } else { // grandparent->right == parent + grandparent->right = addon; + } + } else { + addon->set->root = addon; + } + if (balance == 0) { + break; + } + } } struct wlr_addon *wlr_addon_find(struct wlr_addon_set *set, const void *owner, const struct wlr_addon_interface *impl) { - struct wlr_addon *addon; - wl_list_for_each(addon, &set->addons, link) { - if (addon->owner == owner && addon->impl == impl) { + struct wlr_addon *addon = set->root; + while (addon != NULL) { + ptrdiff_t diff = compare(addon, owner, impl); + if (diff < 0) { + addon = addon->left; + } else if (diff > 0) { + addon = addon->right; + } else { return addon; } }