diff --git a/include/target-utils/rbt/rbt.h b/include/target-utils/rbt/rbt.h new file mode 100644 index 0000000..707e0d0 --- /dev/null +++ b/include/target-utils/rbt/rbt.h @@ -0,0 +1,204 @@ +/* + * Copyright(c) 2024 Intel Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#define BLACK 0 +#define RED 1 + +typedef enum bf_rbt_sts_t { + BF_RBT_OK, + BF_RBT_ERR, + BF_RBT_NO_KEY, + BF_RBT_KEY_EXISTS +} bf_rbt_sts_t; + +enum rbt_node_direction { + BF_RBT_LEFT_NODE, + BF_RBT_RIGHT_NODE, + BF_RBT_ROOT_NODE +}; + +typedef struct bf_rbt_node_t { + uint32_t priority; + bool color; + struct bf_rbt_node_t *left, *right, *parent; + void *data; +} bf_rbt_node_t; + +/*! + * Create a tree node for the priority + * + * @param priority parsed from match spec + * @param root parent node of new node to be created + * @return pointer to newly created node + */ +bf_rbt_node_t *bf_create_rbt_node(uint32_t priority, bf_rbt_node_t *root); + +/*! + * Perform a right rotation of node + * + * @param node around which rotation happens + * @param rbt_head head ptr of rb-tree + * @return pointer to node after rotation + */ +bf_rbt_node_t *bf_right_rotate_rbt_node(bf_rbt_node_t *node, bf_rbt_node_t **rbt_head); + +/*! + * Perform a left rotation of node + * + * @param node around which rotation happens + * @param rbt_head head ptr of rb-tree + * @return pointer to node after rotation + */ +bf_rbt_node_t *bf_left_rotate_rbt_node(bf_rbt_node_t *node, bf_rbt_node_t **rbt_head); + +/*! + * Call left/right rotate APIs based on parent-child node direction + * + * @param parent node based on which direction of rotation is evaluated + * @param rbt_head head ptr of rb-tree + * @param key priority of child node + * @return pointer to node after rotation + */ +bf_rbt_node_t *bf_perform_rotation(bf_rbt_node_t *parent, bf_rbt_node_t **rbt_head, uint32_t key); + +/*! + * Fix RBT node colors as part of balancing tree + * + * @param parent node based on which direction of rotation is evaluated + * @param key priority of child node + * @param key priority of child node + * @return void + */ +void bf_color_fix_rbt_nodes(bf_rbt_node_t *parent, bf_rbt_node_t **rbt_head, uint32_t key); + +/*! + * Retrieve color of neighbor node + * + * @param root node for which it is necessary to determine its neighboring node color. + * @return color of neighbor node RED/BLACK + */ +bool bf_get_rbt_neigh_color(bf_rbt_node_t *root); + +/*! + * Retrieve neighbor node for given node + * + * @param root node for which it is necessary to determine its neighboring node. + * @return pointer to neighbor node + */ +bf_rbt_node_t *bf_get_neighbor_rbt_node(bf_rbt_node_t *root); + +/*! + * Retrieve direction of given node + * + * @param root child node for which it is necessary to determine its direction. + * @return direction left/right child + */ +int bf_get_rbt_node_direction(bf_rbt_node_t *root); + +/*! + * Retrieve inorder successor node for given node + * + * @param root node for which it is necessary to determine its successor node. + * @return pointer to successor node + */ +bf_rbt_node_t *bf_get_successor_rbt_node(bf_rbt_node_t *root); + +/*! + * Retrieve inorder predecessor node for given node + * + * @param root node for which it is necessary to determine its predecessor node. + * @return pointer to predecessor node + */ +bf_rbt_node_t *bf_get_predecessor_rbt_node(bf_rbt_node_t *root); + +/*! + * Insert a tree node into RBT with given priority + * + * @param key priority to be inserted into RBT + * @param root parent node of new node to be created + * @return pointer to newly created node + */ +bf_rbt_node_t *bf_insert_rbt_entry(bf_rbt_node_t *root, uint32_t key, bf_rbt_node_t **rbt_head); + +/*! + * Rtrieve node which is just lesser than or equal to given priority + * + * @param priority for which lower bound has to be found + * @param rbt_head head ptr of rb-tree + * @return pointer to lower bound node + */ +bf_rbt_node_t *bf_get_lower_bound(uint32_t priority, bf_rbt_node_t *rbt_head); + +/*! + * Retrieve node which is just greater than or equal to given priority + * + * @param priority for which upper bound has to be found + * @param rbt_head head ptr of rb-tree + * @return pointer to upper bound node + */ +bf_rbt_node_t *bf_get_upper_bound(uint32_t priority, bf_rbt_node_t *rbt_head); + +/*! + * Retrieve node with highest priority in rb-tree + * + * @param rbt_head head ptr of rb-tree + * @return pointer to highest priority node + */ +bf_rbt_node_t *bf_get_highest_priority_node(bf_rbt_node_t *rbt_head); + +/*! + * Retrieve node with lowest priority in rb-tree + * + * @param rbt_head head ptr of rb-tree + * @return pointer to lowest priority node + */ +bf_rbt_node_t *bf_get_lowest_priority_node(bf_rbt_node_t *rbt_head); + +/*! + * Perform BST node deletion for given key and update color information + * + * @param key priority to be deleted from RBT + * @param color address where color needs to be updated + * @return node which has to be deleted + */ +bf_rbt_node_t *bf_bst_node_deletion(uint32_t key, bf_rbt_node_t *rbt_head, int *color); + +/*! + * Remove a tree node from RBT with given priority + * + * @param key priority of node to be deleted from RBT + * @return status of API call + */ +int bf_remove_rbt_entry(uint32_t key, bf_rbt_node_t **rbt_head); + +/*! + * Balance RBT after insertion of new node + * + * @param root parent of the node which got inserted + * @param key priority of inserted node + * @return void + */ +void bf_balance_rbt_post_insertion(bf_rbt_node_t *root, bf_rbt_node_t **rbt_head, uint32_t key); + +/*! + * Balance RBT after deletion of a node + * + * @param node node to be deleted from RBT + * @return void + */ +void bf_balance_rbt_post_deletion(bf_rbt_node_t *node, bf_rbt_node_t **rbt_head); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6d46687..521b571 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,5 +7,6 @@ add_library(target_sysutil_o OBJECT fbitset/fbitset.c id/id.c map/map.c + rbt/rbt.c power2_allocator/power2_allocator.c ) diff --git a/src/rbt/rbt.c b/src/rbt/rbt.c new file mode 100644 index 0000000..1db49a5 --- /dev/null +++ b/src/rbt/rbt.c @@ -0,0 +1,462 @@ +/* + * Copyright(c) 2024 Intel Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +bf_rbt_node_t *bf_create_rbt_node(uint32_t priority, bf_rbt_node_t *root) { + bf_rbt_node_t *new_node = (bf_rbt_node_t *)bf_sys_calloc(1, sizeof(bf_rbt_node_t)); + if (new_node == NULL) + return NULL; + new_node->priority = priority; + new_node->color = RED; + new_node->left = new_node->right = NULL; + new_node->parent = root; + return new_node; +} + +bf_rbt_node_t *bf_right_rotate_rbt_node(bf_rbt_node_t *node, bf_rbt_node_t **rbt_head) { + bf_rbt_node_t *g_parent = node->parent; + bf_rbt_node_t *l_child = node->left; + bf_rbt_node_t *g_child = l_child->right; + bool c_color = l_child->color; + bool p_color = node->color; + + l_child->right = node; + l_child->parent = g_parent; + node->left = g_child; + node->parent = l_child; + l_child->color = p_color; + node->color = c_color; + + if (g_child != NULL) + g_child->parent = node; + + if (g_parent != NULL) { + if (g_parent->priority < l_child->priority) + g_parent->right = l_child; + else + g_parent->left = l_child; + } + + if (node == *rbt_head) + *rbt_head = l_child; + + return l_child; +} + +bf_rbt_node_t *bf_left_rotate_rbt_node(bf_rbt_node_t *node, bf_rbt_node_t **rbt_head) { + bf_rbt_node_t *g_parent = node->parent; + bf_rbt_node_t *r_child = node->right; + bf_rbt_node_t *g_child = r_child->left; + bool p_color = node->color; + bool c_color = r_child->color; + + r_child->left = node; + r_child->parent = g_parent; + node->right = g_child; + node->parent = r_child; + r_child->color = p_color; + node->color = c_color; + + if (g_child != NULL) + g_child->parent = node; + + if (g_parent != NULL) { + if (g_parent->priority < r_child->priority) + g_parent->right = r_child; + else + g_parent->left = r_child; + } + + if (node == *rbt_head) + *rbt_head = r_child; + + return r_child; +} + +bf_rbt_node_t *bf_perform_rotation(bf_rbt_node_t *parent, bf_rbt_node_t **rbt_head, uint32_t key) { + bf_rbt_node_t *g_parent = parent->parent; + /* If g_parent->priority < parent->priority, + * then parent is right child to grand parent + */ + if (g_parent->priority < parent->priority) { + /* If parent->priority < key, then new node is right child to parent + * Perform left rotation + */ + if (parent->priority < key) { + parent = bf_left_rotate_rbt_node(g_parent, rbt_head); + } + /* If new node is left child to parent. It needs 2 rotations. + * Right rotate parent, left rotate grand parent node + */ + else { + parent = bf_right_rotate_rbt_node(parent, rbt_head); + parent = bf_left_rotate_rbt_node(g_parent, rbt_head); + } + } + /* If g_parent->priority > parent->priority, + * then parent is left child to grand parent + */ + else { + /* If parent->priority < key, then new node is right child to parent + * Left rotate parent, right rotate grand parent node + */ + if (parent->priority < key) { + parent = bf_left_rotate_rbt_node(parent, rbt_head); + parent = bf_right_rotate_rbt_node(g_parent, rbt_head); + } + /* If new node is left child to parent + * Perform right rotation + */ + else { + parent = bf_right_rotate_rbt_node(g_parent, rbt_head); + } + } + return parent; +} + +bf_rbt_node_t *bf_get_successor_rbt_node(bf_rbt_node_t *root) { + bf_rbt_node_t *successor = root->right; + if (successor == NULL) + return NULL; + while (successor->left != NULL) + successor = successor->left; + return successor; +} + +bf_rbt_node_t *bf_get_predecessor_rbt_node(bf_rbt_node_t *root) { + bf_rbt_node_t *predecessor = root->left; + if (predecessor == NULL) + return NULL; + while (predecessor->right != NULL) + predecessor = predecessor->right; + return predecessor; +} + +bf_rbt_node_t *bf_get_lower_bound(uint32_t priority, bf_rbt_node_t *rbt_head) { + bf_rbt_node_t *lower_bound = NULL; + bf_rbt_node_t *root = rbt_head; + + while (root != NULL) { + if (root->priority == priority) { + return root; // Exact match found + } else if (root->priority > priority) { + root = root->left; + } else { + lower_bound = root; // Update result and move to the right subtree + root = root->right; + } + } + + return lower_bound; +} + +bf_rbt_node_t *bf_get_upper_bound(uint32_t priority, bf_rbt_node_t *rbt_head) { + bf_rbt_node_t *upper_bound = NULL; + bf_rbt_node_t *root = rbt_head; + + while (root != NULL) { + if (root->priority == priority) { + return root; // Exact match found + } else if (root->priority < priority) { + root = root->right; + } else { + upper_bound = root; // Update result and move to the left subtree + root = root->left; + } + } + + return upper_bound; +} + +bf_rbt_node_t *bf_get_highest_priority_node(bf_rbt_node_t *rbt_head) { + bf_rbt_node_t *current = rbt_head; + if (current == NULL) + return NULL; + + // Traverse to the rightmost node + while (current->right != NULL) { + current = current->right; + } + + return current; +} + +bf_rbt_node_t *bf_get_lowest_priority_node(bf_rbt_node_t *rbt_head) { + bf_rbt_node_t *current = rbt_head; + if (current == NULL) + return NULL; + + // Traverse to the leftmost node + while (current->left != NULL) { + current = current->left; + } + + return current; +} + +int bf_get_rbt_node_direction(bf_rbt_node_t *root) { + if (root->parent == NULL) + return BF_RBT_ROOT_NODE; + if (root->parent->priority < root->priority || + (root->parent->right != NULL && root->parent->right->priority == root->priority)) + return BF_RBT_RIGHT_NODE; + return BF_RBT_LEFT_NODE; +} + +bool bf_get_rbt_neigh_color(bf_rbt_node_t *root) { + bf_rbt_node_t *parent = root->parent; + if (parent == NULL) + return BLACK; + if ((parent->right != NULL && parent->right->priority == root->priority) || + parent->priority < root->priority) { + if (parent->left != NULL) + return parent->left->color; + } else if ((parent->left != NULL && parent->left->priority == root->priority) || + parent->priority > root->priority) { + if (parent->right != NULL) + return parent->right->color; + } + return BLACK; +} + +bf_rbt_node_t *bf_get_neighbor_rbt_node(bf_rbt_node_t *root) { + bf_rbt_node_t *parent = root->parent; + if (parent == NULL) + return NULL; + if ((parent->right != NULL && parent->right->priority == root->priority) || + parent->priority < root->priority) { + return parent->left; + } else if ((parent->left != NULL && parent->left->priority == root->priority) || + parent->priority > root->priority) { + return parent->right; + } + return NULL; +} + +void bf_color_fix_rbt_nodes(bf_rbt_node_t *root, bf_rbt_node_t **rbt_head, uint32_t key) { + if (root->color == BLACK) + return; + bf_rbt_node_t *parent = root->parent; + bf_rbt_node_t *sibling; + if(parent->priority < root->priority) { + sibling = parent->left; + } else { + sibling = parent->right; + } + root->color = sibling->color = BLACK; + if (parent->parent == NULL) { + parent->color = BLACK; + return; + } else { + parent->color = RED; + //Color fixing should be done from current node to top until reach root node + bf_balance_rbt_post_insertion(parent->parent, rbt_head, root->priority); + } +} + +void bf_balance_rbt_post_insertion(bf_rbt_node_t *root, bf_rbt_node_t **rbt_head, uint32_t key) { + bool neigh_color; + if (root->color == RED) { + neigh_color = bf_get_rbt_neigh_color(root); + /* If neighbor node color is RED, perform + * recoloring upper node to balance RB Tree + */ + if (neigh_color == RED) + bf_color_fix_rbt_nodes(root, rbt_head, key); + /* If neighbor node color is BLACK, perform + * rotation to balance the RB Tree + */ + else + bf_perform_rotation(root, rbt_head, key); + } +} + +bf_rbt_node_t *bf_insert_rbt_entry(bf_rbt_node_t *root, uint32_t key, bf_rbt_node_t **rbt_head) { + bf_rbt_node_t *prev = root; + bf_rbt_node_t *res_node; + /* If tree is empty, create new node and update it's color to black + * root node should be always black + */ + if (root == NULL) { + bf_rbt_node_t *new_node = bf_create_rbt_node(key, root); + if (new_node == NULL) + return NULL; + new_node->color = BLACK; + *rbt_head = new_node; + return new_node; + } + while (root != NULL) { + prev = root; + if (root->priority < key) + root = root->right; + else if (root->priority > key) + root = root->left; + else + break; + } + if (root == NULL) + root = prev; + if (root->priority == key) { + return root; + } else if (root->priority < key) { + root->right = bf_create_rbt_node(key, root); + if (root->right == NULL) + return NULL; + res_node = root->right; + bf_balance_rbt_post_insertion(root, rbt_head, key); + } else { + root->left = bf_create_rbt_node(key, root); + if (root->left == NULL) + return NULL; + res_node = root->left; + bf_balance_rbt_post_insertion(root, rbt_head, key); + } + return res_node; +} + +bf_rbt_node_t *bf_bst_node_deletion(uint32_t key, bf_rbt_node_t *rbt_head, int *color) { + bf_rbt_node_t *root_node = rbt_head; + bf_rbt_node_t *replacement; + while (root_node != NULL && root_node->priority != key) { + if (root_node->priority < key) + root_node = root_node->right; + else if (root_node->priority > key) + root_node = root_node->left; + } + while (root_node != NULL) { + /* If the node be deleted is a leaf node, update + * color and just return that node + */ + if (root_node->right == NULL && root_node->left == NULL) { + *color = root_node->color; + return root_node; + } + /* If the node be deleted is an internal node, replace the value + * in the node with either in-order successor/predecessor and return + * in-order successor/predecessor. Repeat the same until reach leaf node + */ + else { + replacement = bf_get_successor_rbt_node(root_node); + if (replacement == NULL) + replacement = bf_get_predecessor_rbt_node(root_node); + root_node->priority = replacement->priority; + root_node->data = replacement->data; + *color = replacement->color; + root_node = replacement; + } + } + return root_node; +} + +void bf_balance_rbt_post_deletion(bf_rbt_node_t *node, bf_rbt_node_t **rbt_head) { + bf_rbt_node_t *neigh_node; + bool imbalance_tree = true; + int node_dir; + while (imbalance_tree != false && node != NULL) { + if (node->parent == NULL) { + imbalance_tree = false; + break; + } + neigh_node = bf_get_neighbor_rbt_node(node); + node_dir = bf_get_rbt_node_direction(node); + if (neigh_node == NULL || neigh_node->color == BLACK) { + /* Check if neigh_node is either NULL or + * doesn't have a RED child + */ + if (neigh_node == NULL || ((neigh_node->right == NULL || neigh_node->right->color == BLACK) && + (neigh_node->left == NULL || neigh_node->left->color == BLACK))) { + if (neigh_node != NULL) + neigh_node->color = RED; + if (node->parent->color == RED) { + node->parent->color = BLACK; + imbalance_tree = false; + } + else { + node = node->parent; + } + } + /* Check if far child from current node is RED which means + * right child of neighbor is RED if current node is in the left + */ + else if (node_dir == BF_RBT_LEFT_NODE && neigh_node->right != NULL && neigh_node->right->color == RED) { + bf_left_rotate_rbt_node(node->parent, rbt_head); + neigh_node->right->color = BLACK; + imbalance_tree = false; + } + /* Mirror copy of above case + * left child of neighbor is RED if current node is in the right + */ + else if (node_dir == BF_RBT_RIGHT_NODE && neigh_node->left != NULL && neigh_node->left->color == RED) { + bf_right_rotate_rbt_node(node->parent, rbt_head); + neigh_node->left->color = BLACK; + imbalance_tree = false; + } + /* Check if far child from current node is BLACK and near child is RED which means + * right child of neighbor is BLACK, left child of neighbor is RED if current node is in the left + */ + else if (node_dir == BF_RBT_LEFT_NODE && (neigh_node->right == NULL || neigh_node->right->color == BLACK) && + (neigh_node->left != NULL && neigh_node->left->color == RED)) { + bf_right_rotate_rbt_node(neigh_node, rbt_head); + } + /* Mirror copy of above case + * right child of neighbor is RED, left child of neighbor is BLACK if current node is in the right + */ + else if (node_dir == BF_RBT_RIGHT_NODE && (neigh_node->left == NULL || neigh_node->left->color == BLACK) && + (neigh_node->right != NULL && neigh_node->right->color == RED)) { + bf_left_rotate_rbt_node(neigh_node, rbt_head); + } + } else { + if (node_dir == BF_RBT_RIGHT_NODE) + bf_right_rotate_rbt_node(node->parent, rbt_head); + else + bf_left_rotate_rbt_node(node->parent, rbt_head); + } + } + if ((*rbt_head)->color == RED) + (*rbt_head)->color = BLACK; +} + +int bf_remove_rbt_entry(uint32_t key, bf_rbt_node_t **rbt_head) { + bf_rbt_node_t *res_node, *parent; + int child_dir; + int color; + //Apply traditional BST deletion and get the node to be deleted + res_node = bf_bst_node_deletion(key, *rbt_head, &color); + if (res_node == NULL) + return BF_RBT_NO_KEY; + /* If the retrieved node is black, + * then RB Tree should be re-balanced + */ + if (color == BLACK) + bf_balance_rbt_post_deletion(res_node, rbt_head); + parent = res_node->parent; + if (parent == NULL) { + *rbt_head = NULL; + } else { + // Update the parent of node to be deleted + child_dir = bf_get_rbt_node_direction(res_node); + if (child_dir == BF_RBT_LEFT_NODE) { + parent->left = NULL; + } else { + parent->right = NULL; + } + } + bf_sys_free(res_node); + res_node = NULL; + return BF_RBT_OK; +}