flycast/core/deps/picotcp/stack/pico_tree.c

566 lines
16 KiB
C
Raw Normal View History

2018-09-15 19:34:50 +00:00
/*********************************************************************
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Author: Andrei Carp <andrei.carp@tass.be>
*********************************************************************/
#include "pico_tree.h"
#include "pico_config.h"
#include "pico_protocol.h"
#include "pico_mm.h"
#define RED 0
#define BLACK 1
/* By default the null leafs are black */
struct pico_tree_node LEAF = {
NULL, /* key */
&LEAF, &LEAF, &LEAF, /* parent, left,right */
BLACK, /* color */
};
#define IS_LEAF(x) (x == &LEAF)
#define IS_NOT_LEAF(x) (x != &LEAF)
#define INIT_LEAF (&LEAF)
#define AM_I_LEFT_CHILD(x) (x == x->parent->leftChild)
#define AM_I_RIGHT_CHILD(x) (x == x->parent->rightChild)
#define PARENT(x) (x->parent)
#define GRANPA(x) (x->parent->parent)
/*
* Local Functions
*/
static struct pico_tree_node *create_node(struct pico_tree *tree, void *key, uint8_t allocator);
static void rotateToLeft(struct pico_tree*tree, struct pico_tree_node*node);
static void rotateToRight(struct pico_tree*root, struct pico_tree_node*node);
static void fix_insert_collisions(struct pico_tree*tree, struct pico_tree_node*node);
static void fix_delete_collisions(struct pico_tree*tree, struct pico_tree_node *node);
static void switchNodes(struct pico_tree*tree, struct pico_tree_node*nodeA, struct pico_tree_node*nodeB);
void *pico_tree_insert_implementation(struct pico_tree *tree, void *key, uint8_t allocator);
void *pico_tree_delete_implementation(struct pico_tree *tree, void *key, uint8_t allocator);
#ifdef PICO_SUPPORT_MM
/* The memory manager also uses the pico_tree to keep track of all the different slab sizes it has.
* These nodes should be placed in the manager page which is in a different memory region then the nodes
* which are used for the pico stack in general.
* Therefore the following 2 functions are created so that pico_tree can use them to to put these nodes
* into the correct memory regions.
* If pico_tree_insert is called from the memory manager module, then create_node should use
* pico_mem_page0_zalloc to create a node. The same for pico_tree_delete.
*/
extern void*pico_mem_page0_zalloc(size_t len);
extern void pico_mem_page0_free(void*ptr);
#endif /* PICO_SUPPORT_MM */
/*
* Exported functions
*/
struct pico_tree_node *pico_tree_firstNode(struct pico_tree_node *node)
{
while(IS_NOT_LEAF(node->leftChild))
node = node->leftChild;
return node;
}
struct pico_tree_node *pico_tree_lastNode(struct pico_tree_node *node)
{
while(IS_NOT_LEAF(node->rightChild))
node = node->rightChild;
return node;
}
struct pico_tree_node *pico_tree_next(struct pico_tree_node *node)
{
if (!node)
return NULL;
if(IS_NOT_LEAF(node->rightChild))
{
node = node->rightChild;
while(IS_NOT_LEAF(node->leftChild))
node = node->leftChild;
}
else
{
if (IS_NOT_LEAF(node->parent) && AM_I_LEFT_CHILD(node))
node = node->parent;
else {
while (IS_NOT_LEAF(node->parent) && AM_I_RIGHT_CHILD(node))
node = node->parent;
node = node->parent;
}
}
return node;
}
struct pico_tree_node *pico_tree_prev(struct pico_tree_node *node)
{
if (IS_NOT_LEAF(node->leftChild)) {
node = node->leftChild;
while (IS_NOT_LEAF(node->rightChild))
node = node->rightChild;
} else {
if (IS_NOT_LEAF(node->parent) && AM_I_RIGHT_CHILD(node))
node = node->parent;
else {
while (IS_NOT_LEAF(node) && AM_I_LEFT_CHILD(node))
node = node->parent;
node = node->parent;
}
}
return node;
}
/* The memory manager also uses the pico_tree to keep track of all the different slab sizes it has.
* These nodes should be placed in the manager page which is in a different memory region then the nodes
* which are used for the pico stack in general.
* Therefore the following wrapper for pico_tree_insert is created.
* The actual implementation can be found in pico_tree_insert_implementation.
*/
void *pico_tree_insert(struct pico_tree *tree, void *key)
{
return pico_tree_insert_implementation(tree, key, USE_PICO_ZALLOC);
}
static void pico_tree_insert_node(struct pico_tree *tree, struct pico_tree_node *insert)
{
struct pico_tree_node *temp = tree->root;
struct pico_tree_node *last_node = INIT_LEAF;
int result = 0;
/* search for the place to insert the new node */
while(IS_NOT_LEAF(temp))
{
last_node = temp;
result = tree->compare(insert->keyValue, temp->keyValue);
temp = (result < 0) ? (temp->leftChild) : (temp->rightChild);
}
/* make the needed connections */
insert->parent = last_node;
if(IS_LEAF(last_node))
tree->root = insert;
else{
result = tree->compare(insert->keyValue, last_node->keyValue);
if(result < 0)
last_node->leftChild = insert;
else
last_node->rightChild = insert;
}
}
void *pico_tree_insert_implementation(struct pico_tree *tree, void *key, uint8_t allocator)
{
struct pico_tree_node *insert;
void *LocalKey;
LocalKey = (IS_NOT_LEAF(tree->root) ? pico_tree_findKey(tree, key) : NULL);
/* if node already in, bail out */
if(LocalKey) {
pico_err = PICO_ERR_EEXIST;
return LocalKey;
}
insert = create_node(tree, key, allocator);
if(!insert)
{
pico_err = PICO_ERR_ENOMEM;
/* to let the user know that it couldn't insert */
return (void *)&LEAF;
}
pico_tree_insert_node(tree, insert);
/* fix colour issues */
fix_insert_collisions(tree, insert);
return NULL;
}
struct pico_tree_node *pico_tree_findNode(struct pico_tree *tree, void *key)
{
struct pico_tree_node *found;
found = tree->root;
while(IS_NOT_LEAF(found))
{
int result;
result = tree->compare(found->keyValue, key);
if(result == 0)
return found;
else if(result < 0)
found = found->rightChild;
else
found = found->leftChild;
}
return NULL;
}
void *pico_tree_findKey(struct pico_tree *tree, void *key)
{
struct pico_tree_node *found;
found = pico_tree_findNode(tree, key);
if (found == NULL)
return NULL;
return found->keyValue;
}
void *pico_tree_first(struct pico_tree *tree)
{
return pico_tree_firstNode(tree->root)->keyValue;
}
void *pico_tree_last(struct pico_tree *tree)
{
return pico_tree_lastNode(tree->root)->keyValue;
}
static uint8_t pico_tree_delete_node(struct pico_tree *tree, struct pico_tree_node *d, struct pico_tree_node **temp)
{
struct pico_tree_node *min;
struct pico_tree_node *ltemp = d;
uint8_t nodeColor;
min = pico_tree_firstNode(d->rightChild);
nodeColor = min->color;
*temp = min->rightChild;
if(min->parent == ltemp && IS_NOT_LEAF(*temp))
(*temp)->parent = min;
else{
switchNodes(tree, min, min->rightChild);
min->rightChild = ltemp->rightChild;
if(IS_NOT_LEAF(min->rightChild)) min->rightChild->parent = min;
}
switchNodes(tree, ltemp, min);
min->leftChild = ltemp->leftChild;
if(IS_NOT_LEAF(min->leftChild))
min->leftChild->parent = min;
min->color = ltemp->color;
return nodeColor;
}
static uint8_t pico_tree_delete_check_switch(struct pico_tree *tree, struct pico_tree_node *delete, struct pico_tree_node **temp)
{
struct pico_tree_node *ltemp = delete;
uint8_t nodeColor = delete->color;
if(IS_LEAF(delete->leftChild))
{
*temp = ltemp->rightChild;
switchNodes(tree, ltemp, ltemp->rightChild);
}
else
if(IS_LEAF(delete->rightChild))
{
struct pico_tree_node *_ltemp = delete;
*temp = _ltemp->leftChild;
switchNodes(tree, _ltemp, _ltemp->leftChild);
}
else{
nodeColor = pico_tree_delete_node(tree, delete, temp);
}
return nodeColor;
}
/* The memory manager also uses the pico_tree to keep track of all the different slab sizes it has.
* These nodes should be placed in the manager page which is in a different memory region then the nodes
* which are used for the pico stack in general.
* Therefore the following wrapper for pico_tree_delete is created.
* The actual implementation can be found in pico_tree_delete_implementation.
*/
void *pico_tree_delete(struct pico_tree *tree, void *key)
{
return pico_tree_delete_implementation(tree, key, USE_PICO_ZALLOC);
}
static inline void if_nodecolor_black_fix_collisions(struct pico_tree *tree, struct pico_tree_node *temp, uint8_t nodeColor)
{
/* deleted node is black, this will mess up the black path property */
if(nodeColor == BLACK)
fix_delete_collisions(tree, temp);
}
void *pico_tree_delete_implementation(struct pico_tree *tree, void *key, uint8_t allocator)
{
struct pico_tree_node *temp;
uint8_t nodeColor; /* keeps the color of the node to be deleted */
void *lkey; /* keeps a copy of the key which will be removed */
struct pico_tree_node *delete; /* keeps a copy of the node to be extracted */
if (!key)
return NULL;
delete = pico_tree_findNode(tree, key);
/* this key isn't in the tree, bail out */
if(!delete)
return NULL;
lkey = delete->keyValue;
nodeColor = pico_tree_delete_check_switch(tree, delete, &temp);
if_nodecolor_black_fix_collisions(tree, temp, nodeColor);
if(allocator == USE_PICO_ZALLOC)
PICO_FREE(delete);
#ifdef PICO_SUPPORT_MM
else
pico_mem_page0_free(delete);
#endif
return lkey;
}
int pico_tree_empty(struct pico_tree *tree)
{
return (!tree->root || IS_LEAF(tree->root));
}
/*
* Private functions
*/
static void rotateToLeft(struct pico_tree*tree, struct pico_tree_node*node)
{
struct pico_tree_node*temp;
temp = node->rightChild;
if(temp == &LEAF) return;
node->rightChild = temp->leftChild;
if(IS_NOT_LEAF(temp->leftChild))
temp->leftChild->parent = node;
temp->parent = node->parent;
if(IS_LEAF(node->parent))
tree->root = temp;
else
if(node == node->parent->leftChild)
node->parent->leftChild = temp;
else
node->parent->rightChild = temp;
temp->leftChild = node;
node->parent = temp;
}
static void rotateToRight(struct pico_tree *tree, struct pico_tree_node *node)
{
struct pico_tree_node*temp;
temp = node->leftChild;
node->leftChild = temp->rightChild;
if(temp == &LEAF) return;
if(IS_NOT_LEAF(temp->rightChild))
temp->rightChild->parent = node;
temp->parent = node->parent;
if(IS_LEAF(node->parent))
tree->root = temp;
else
if(node == node->parent->rightChild)
node->parent->rightChild = temp;
else
node->parent->leftChild = temp;
temp->rightChild = node;
node->parent = temp;
return;
}
static struct pico_tree_node *create_node(struct pico_tree *tree, void*key, uint8_t allocator)
{
struct pico_tree_node *temp = NULL;
IGNORE_PARAMETER(tree);
if(allocator == USE_PICO_ZALLOC)
temp = (struct pico_tree_node *)PICO_ZALLOC(sizeof(struct pico_tree_node));
#ifdef PICO_SUPPORT_MM
else
temp = (struct pico_tree_node *)pico_mem_page0_zalloc(sizeof(struct pico_tree_node));
#endif
if(!temp)
return NULL;
temp->keyValue = key;
temp->parent = &LEAF;
temp->leftChild = &LEAF;
temp->rightChild = &LEAF;
/* by default every new node is red */
temp->color = RED;
return temp;
}
/*
* This function fixes the possible collisions in the tree.
* Eg. if a node is red his children must be black !
*/
static void fix_insert_collisions(struct pico_tree*tree, struct pico_tree_node*node)
{
struct pico_tree_node*temp;
while(node->parent->color == RED && IS_NOT_LEAF(GRANPA(node)))
{
if(AM_I_RIGHT_CHILD(node->parent))
{
temp = GRANPA(node)->leftChild;
if(temp->color == RED) {
node->parent->color = BLACK;
temp->color = BLACK;
GRANPA(node)->color = RED;
node = GRANPA(node);
}
else if(temp->color == BLACK) {
if(AM_I_LEFT_CHILD(node)) {
node = node->parent;
rotateToRight(tree, node);
}
node->parent->color = BLACK;
GRANPA(node)->color = RED;
rotateToLeft(tree, GRANPA(node));
}
}
else if(AM_I_LEFT_CHILD(node->parent))
{
temp = GRANPA(node)->rightChild;
if(temp->color == RED) {
node->parent->color = BLACK;
temp->color = BLACK;
GRANPA(node)->color = RED;
node = GRANPA(node);
}
else if(temp->color == BLACK) {
if(AM_I_RIGHT_CHILD(node)) {
node = node->parent;
rotateToLeft(tree, node);
}
node->parent->color = BLACK;
GRANPA(node)->color = RED;
rotateToRight(tree, GRANPA(node));
}
}
}
/* make sure that the root of the tree stays black */
tree->root->color = BLACK;
}
static void switchNodes(struct pico_tree*tree, struct pico_tree_node*nodeA, struct pico_tree_node*nodeB)
{
if(IS_LEAF(nodeA->parent))
tree->root = nodeB;
else
if(IS_NOT_LEAF(nodeA))
{
if(AM_I_LEFT_CHILD(nodeA))
nodeA->parent->leftChild = nodeB;
else
nodeA->parent->rightChild = nodeB;
}
if(IS_NOT_LEAF(nodeB)) nodeB->parent = nodeA->parent;
}
/*
* This function fixes the possible collisions in the tree.
* Eg. if a node is red his children must be black !
* In this case the function fixes the constant black path property.
*/
static void fix_delete_collisions(struct pico_tree*tree, struct pico_tree_node *node)
{
struct pico_tree_node*temp;
while( node != tree->root && node->color == BLACK && IS_NOT_LEAF(node))
{
if(AM_I_LEFT_CHILD(node)) {
temp = node->parent->rightChild;
if(temp->color == RED)
{
temp->color = BLACK;
node->parent->color = RED;
rotateToLeft(tree, node->parent);
temp = node->parent->rightChild;
}
if(temp->leftChild->color == BLACK && temp->rightChild->color == BLACK)
{
temp->color = RED;
node = node->parent;
}
else
{
if(temp->rightChild->color == BLACK)
{
temp->leftChild->color = BLACK;
temp->color = RED;
rotateToRight(tree, temp);
temp = temp->parent->rightChild;
}
temp->color = node->parent->color;
node->parent->color = BLACK;
temp->rightChild->color = BLACK;
rotateToLeft(tree, node->parent);
node = tree->root;
}
}
else{
temp = node->parent->leftChild;
if(temp->color == RED)
{
temp->color = BLACK;
node->parent->color = RED;
rotateToRight(tree, node->parent);
temp = node->parent->leftChild;
}
if(temp->rightChild->color == BLACK && temp->leftChild->color == BLACK)
{
temp->color = RED;
node = node->parent;
}
else{
if(temp->leftChild->color == BLACK)
{
temp->rightChild->color = BLACK;
temp->color = RED;
rotateToLeft(tree, temp);
temp = temp->parent->leftChild;
}
temp->color = node->parent->color;
node->parent->color = BLACK;
temp->leftChild->color = BLACK;
rotateToRight(tree, node->parent);
node = tree->root;
}
}
}
node->color = BLACK;
}