641 lines
13 KiB
C
641 lines
13 KiB
C
// ::DataStructures::Globals::Start::
|
|
|
|
RBNode RB_NIL = { .color = RB_BLACK };
|
|
RBNode *P_RB_NIL = &RB_NIL;
|
|
|
|
RBDataNode RB_DN_NIL = {0};
|
|
RBDataNode *P_RB_DN_NIL = &RB_DN_NIL;
|
|
|
|
HashNode HT_NIL = {0};
|
|
HashNode *P_HT_NIL = &HT_NIL;
|
|
|
|
// ::DataStructures::Globals::End::
|
|
|
|
|
|
|
|
// ::DataStructures::RedBlackTree::Functions::Start::
|
|
|
|
static void RBTreeInit(RBTree *tree)
|
|
{
|
|
Assert(tree != NULL, "RBTree is null");
|
|
RB_NIL.right = RB_NIL.left = RB_NIL.parent = P_RB_NIL;
|
|
RB_DN_NIL.next = P_RB_DN_NIL;
|
|
|
|
tree->root = P_RB_NIL;
|
|
tree->nil = P_RB_NIL;
|
|
}
|
|
|
|
static inline void RBTreePushDataNode(RBDataNode *first, RBDataNode *last, rawptr value)
|
|
{
|
|
RBDataNode *data_node = FLMemAllocZeroed(sizeof(RBDataNode));
|
|
data_node->data = value;
|
|
RBQueuePush(first, last, data_node);
|
|
}
|
|
|
|
static inline RBNode *RBTreeInitNode(u64 key, rawptr value)
|
|
{
|
|
RBNode *node = FLMemAllocZeroed(sizeof(RBNode));
|
|
node->parent = node->left = node->right = P_RB_NIL;
|
|
node->color = RB_BLACK;
|
|
node->key = key;
|
|
|
|
RBTreePushDataNode(node->bucket.first, node->bucket.last, value);
|
|
|
|
return node;
|
|
}
|
|
|
|
static void RBTreeInsert(RBTree *tree, u64 key, rawptr value)
|
|
{
|
|
RBNode *node = P_RB_NIL;
|
|
|
|
node->left = node->right = tree->nil;
|
|
node->color = RB_RED;
|
|
|
|
if (tree->root == tree->nil)
|
|
{
|
|
node->color = RB_BLACK;
|
|
node->parent = tree->nil;
|
|
tree->root = node;
|
|
}
|
|
else
|
|
{
|
|
RBNode *curr_node = tree->root;
|
|
while (true)
|
|
{
|
|
Assert(curr_node != tree->nil, "Current Node is NIL");
|
|
|
|
if (curr_node->key == key)
|
|
{
|
|
RBTreePushDataNode(curr_node->bucket.first, curr_node->bucket.last, value);
|
|
break;
|
|
}
|
|
else if (curr_node->key < key)
|
|
{
|
|
if (curr_node->right == tree->nil)
|
|
{
|
|
node = RBTreeInitNode(key, value);
|
|
node->parent = curr_node;
|
|
curr_node->right = node;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
curr_node = curr_node->right;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (curr_node->left == tree->nil)
|
|
{
|
|
node = RBTreeInitNode(key, value);
|
|
node->parent = curr_node;
|
|
curr_node->left = node;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
curr_node = curr_node->left;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (node->parent->color != RB_BLACK && node != P_RB_NIL)
|
|
RBTreeCorrect(tree, node);
|
|
}
|
|
|
|
static void RBTreeCorrect(RBTree *tree, RBNode *node)
|
|
{
|
|
RBNode *gp = node->parent->parent;
|
|
RBNode *p = node->parent;
|
|
do
|
|
{
|
|
if (node == tree->root)
|
|
{
|
|
node->color = RB_BLACK;
|
|
break;
|
|
}
|
|
|
|
if (gp == tree->nil)
|
|
{
|
|
p->color = RB_BLACK;
|
|
break;
|
|
}
|
|
|
|
RBNodeDir dir = NodeDir(p);
|
|
RBNode *unc = gp->child[1 - dir];
|
|
if (unc == tree->nil || unc->color == RB_BLACK)
|
|
{
|
|
if (node == p->child[1 - dir])
|
|
{
|
|
RBTreeRotate(tree, p, dir);
|
|
node = p;
|
|
p = gp->child[dir];
|
|
}
|
|
|
|
RBTreeRotate(tree, gp, 1 - dir);
|
|
p->color = RB_BLACK;
|
|
gp->color = RB_RED;
|
|
break;
|
|
}
|
|
|
|
p->color = RB_BLACK;
|
|
unc->color = RB_BLACK;
|
|
gp->color = RB_RED;
|
|
node = gp;
|
|
gp = node->parent->parent;
|
|
} while ((p = node->parent));
|
|
}
|
|
|
|
static void RBTreeDelete(RBTree *tree, u64 key, rawptr value)
|
|
{
|
|
RBNode *node = NULL;
|
|
Assert(RBTreeSearch(tree, key, &node), "Unable to find node in RBTreeDelete");
|
|
|
|
if (node->bucket.first->data != value)
|
|
{
|
|
Assert(node->bucket.first->next != P_RB_DN_NIL, "RBTreeDelete Failure: unable to find value to delete");
|
|
RBDataNode *data_node = node->bucket.first->next;
|
|
RBDataNode *prev_node = node->bucket.first;
|
|
|
|
while (data_node != P_RB_DN_NIL)
|
|
{
|
|
if (data_node->data == value)
|
|
{
|
|
prev_node->next = data_node->next;
|
|
if (data_node == node->bucket.last)
|
|
node->bucket.last = prev_node;
|
|
|
|
FLMemFree(data_node);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FLMemFree(node->bucket.first);
|
|
|
|
if (node == tree->root && node->left == tree->nil && node->right == tree->nil)
|
|
{
|
|
tree->root = tree->nil;
|
|
}
|
|
else if (node->left != tree->nil && node->right != tree->nil)
|
|
{
|
|
RBNode *ln = node->right;
|
|
|
|
while (ln->left != tree->nil)
|
|
ln = ln->left;
|
|
|
|
node->key = ln->key;
|
|
node->bucket = ln->bucket;
|
|
|
|
if (node->right == ln)
|
|
node->right = tree->nil;
|
|
else
|
|
ln->parent->left = tree->nil;
|
|
|
|
ln->parent = tree->nil;
|
|
}
|
|
else if (node->color == RB_BLACK && node->left != tree->nil)
|
|
{
|
|
node->key = node->left->key;
|
|
node->bucket = node->left->bucket;
|
|
node->left = tree->nil;
|
|
}
|
|
else if (node->color == RB_BLACK && node->right != tree->nil)
|
|
{
|
|
node->key = node->right->key;
|
|
node->bucket = node->right->bucket;
|
|
node->right = tree->nil;
|
|
}
|
|
else if (node->color == RB_RED && node->right == tree->nil && node->left == tree->nil)
|
|
{
|
|
RBNodeDir dir = NodeDir(node);
|
|
node->parent->child[dir] = tree->nil;
|
|
}
|
|
else
|
|
{
|
|
RBNode *p = node->parent;
|
|
RBNodeColor col = node->color;
|
|
RBNode *s, *cn, *dn;
|
|
|
|
RBNodeDir dir = NodeDir(node);
|
|
p->child[dir] = tree->nil;
|
|
|
|
goto start_deletion;
|
|
|
|
do
|
|
{
|
|
dir = NodeDir(node);
|
|
start_deletion:
|
|
s = p->child[1 - dir];
|
|
dn = s->child[1 - dir];
|
|
cn = s->child[dir];
|
|
|
|
if (s->color == RB_RED)
|
|
{
|
|
RBTreeRotate(tree, p, dir);
|
|
p->color = RB_RED;
|
|
s->color = RB_BLACK;
|
|
s = cn;
|
|
|
|
dn = s->child[1 - dir];
|
|
if (dn->color == RB_RED)
|
|
goto rotate_sibling;
|
|
cn = s->child[dir];
|
|
if (cn->color == RB_RED)
|
|
goto rotate_parent;
|
|
|
|
s->color = RB_RED;
|
|
p->color = RB_BLACK;
|
|
return;
|
|
}
|
|
|
|
if (dn->color == RB_RED)
|
|
goto rotate_parent;
|
|
|
|
if (cn->color == RB_RED)
|
|
goto rotate_sibling;
|
|
|
|
if (p->color == RB_RED)
|
|
{
|
|
s->color = RB_RED;
|
|
p->color = RB_BLACK;
|
|
return;
|
|
}
|
|
|
|
if (p == tree->nil)
|
|
return;
|
|
|
|
s->color = RB_RED;
|
|
node = p;
|
|
} while ((p = node->parent));
|
|
|
|
rotate_sibling:
|
|
RBTreeRotate(tree, s, 1 - dir);
|
|
s->color = RB_RED;
|
|
cn->color = RB_BLACK;
|
|
dn = s;
|
|
s = cn;
|
|
|
|
rotate_parent:
|
|
RBTreeRotate(tree, p, dir);
|
|
s->color = p->color;
|
|
p->color = RB_BLACK;
|
|
dn->color = RB_BLACK;
|
|
}
|
|
}
|
|
}
|
|
|
|
static b32 RBTreeSearchNearest(RBTree *tree, u64 key, RBNode **out_node)
|
|
{
|
|
if (tree->root == tree->nil) return false;
|
|
|
|
RBNode *node = tree->root;
|
|
RBNode *nearest = tree->root;
|
|
u64 nearest_diff = UINT64_MAX;
|
|
|
|
while (true)
|
|
{
|
|
if (node == tree->nil)
|
|
break;
|
|
|
|
u64 diff = node->key - key;
|
|
diff = Absu64(diff);
|
|
|
|
if (diff == 0)
|
|
{
|
|
nearest = node;
|
|
break;
|
|
}
|
|
|
|
if (diff < nearest_diff)
|
|
{
|
|
nearest_diff = diff;
|
|
nearest = node;
|
|
}
|
|
|
|
if (node->key < key)
|
|
node = node->right;
|
|
else
|
|
node = node->left;
|
|
}
|
|
|
|
*out_node = nearest != tree->nil ? nearest : tree->nil;
|
|
|
|
return *out_node != tree->nil;
|
|
}
|
|
|
|
static b32 RBTreeSearch(RBTree *tree, u64 key, RBNode **out_node)
|
|
{
|
|
if (tree->root == tree->nil) return false;
|
|
|
|
b32 found = false;
|
|
RBNode *node = tree->root;
|
|
|
|
while (true)
|
|
{
|
|
if (node->key == key)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
|
|
if (node == tree->nil)
|
|
break;
|
|
|
|
if (node->key < key)
|
|
node = node->right;
|
|
else
|
|
node = node->left;
|
|
}
|
|
|
|
if (found)
|
|
*out_node = node;
|
|
|
|
return found;
|
|
}
|
|
|
|
static inline void RBTreeTransplant(RBTree *tree, RBNode *node, RBNode *placed_node)
|
|
{
|
|
if (node->parent == tree->nil)
|
|
tree->root = placed_node;
|
|
else if (node == node->parent->left)
|
|
node->parent->left = placed_node;
|
|
else
|
|
node->parent->right = placed_node;
|
|
|
|
placed_node->parent = node->parent;
|
|
}
|
|
|
|
static void RBTreeRotate(RBTree *tree, RBNode *node, RBNodeDir dir)
|
|
{
|
|
RBNode *p = node->parent;
|
|
RBNode *root = node->child[1 - dir];
|
|
RBNode *child = root->child[dir];
|
|
|
|
node->child[1 - dir] = child;
|
|
|
|
if (child != tree->nil)
|
|
child->parent = node;
|
|
|
|
root->child[dir] = node;
|
|
root->parent = p;
|
|
node->parent = root;
|
|
|
|
if (p != tree->nil)
|
|
p->child[node == p->right] = root;
|
|
else
|
|
tree->root = root;
|
|
}
|
|
|
|
static void RBTreeLeftRotate(RBTree *tree, RBNode *node)
|
|
{
|
|
RBNode *right = node->right;
|
|
if (right->left != tree->nil)
|
|
node->right = right->left;
|
|
|
|
if (node->parent == tree->nil)
|
|
tree->root = right;
|
|
else if (node->parent->left == node)
|
|
node->parent->left = right;
|
|
else
|
|
node->parent->right = right;
|
|
|
|
right->parent = node->parent;
|
|
right->left = node;
|
|
node->parent = right;
|
|
}
|
|
|
|
static void RBTreeRightRotate(RBTree *tree, RBNode *node)
|
|
{
|
|
RBNode *left = node->left;
|
|
if (left->right != tree->nil)
|
|
node->left = left->right;
|
|
|
|
if (node->parent == tree->nil)
|
|
tree->root = left;
|
|
else if (node->parent->left == node)
|
|
node->parent->left = left;
|
|
else
|
|
node->parent->right = left;
|
|
|
|
left->parent = node->parent;
|
|
left->right = node;
|
|
node->parent = left;
|
|
}
|
|
|
|
// ::DataStructures::RedBlackTree::Functions::End::
|
|
|
|
|
|
|
|
// ::DataStructures::HashTable::Functions::Start::
|
|
|
|
static void HashTableInit(HashTable *table, u32 init_size)
|
|
{
|
|
table->cap = init_size;
|
|
table->count = 0;
|
|
table->free_lists.first = P_HT_NIL;
|
|
table->free_lists.last = P_HT_NIL;
|
|
|
|
table->lists = FLMemAlloc(sizeof(HashList) * init_size);
|
|
for (u32 i = 0; i < init_size; i++)
|
|
{
|
|
table->lists[i].first = P_HT_NIL;
|
|
table->lists[i].last = P_HT_NIL;
|
|
}
|
|
}
|
|
|
|
static void HashTableConcatInPlace(HashList *list, HashList *to_concat)
|
|
{
|
|
SLLConcatInPlaceNoCount(list, to_concat);
|
|
}
|
|
|
|
static void HashTableClear(HashTable *table)
|
|
{
|
|
table->count = 0;
|
|
for (u32 i = 0; i < table->count; i++)
|
|
{
|
|
HashTableConcatInPlace(&table->free_lists, &table->lists[i]);
|
|
}
|
|
}
|
|
|
|
static HashNode *HashListPop(HashList *list)
|
|
{
|
|
HashNode *result = list->first;
|
|
HTQueuePop(list->first, list->last);
|
|
return result;
|
|
}
|
|
|
|
static HashNode *HashTablePush(HashTable *table, u64 hash, KeyValuePair value)
|
|
{
|
|
HashNode *node = NULL;
|
|
if (table->free_lists.first != P_HT_NIL)
|
|
node = HashListPop(&table->free_lists);
|
|
else
|
|
node = FLMemAlloc(sizeof(HashNode));
|
|
|
|
node->next = P_HT_NIL;
|
|
node->v = value;
|
|
|
|
u64 index = hash % table->cap;
|
|
HTQueuePush(table->lists[index].first, table->lists[index].last, node);
|
|
table->count += 1;
|
|
|
|
return node;
|
|
}
|
|
|
|
static HashNode *HashTablePushU64U32(HashTable *table, u64 key, u32 value)
|
|
{
|
|
u64 hash = HashFromString(String8Struct(&key));
|
|
return HashTablePush(table, hash, (KeyValuePair){ .key_u64 = key, .value_u32 = value });
|
|
}
|
|
|
|
static HashNode *HashTablePushU64U64(HashTable *table, u64 key, u64 value)
|
|
{
|
|
u64 hash = HashFromString(String8Struct(&key));
|
|
return HashTablePush(table, hash, (KeyValuePair){ .key_u64 = key, .value_u64 = value });
|
|
}
|
|
|
|
static HashNode *HashTablePushU64String8(HashTable *table, u64 key, String8 value)
|
|
{
|
|
u64 hash = HashFromString(String8Struct(&key));
|
|
return HashTablePush(table, hash, (KeyValuePair){ .key_u64 = key, .value_string = value });
|
|
}
|
|
|
|
static HashNode *HashTablePushU64Rawptr(HashTable *table, u64 key, rawptr value)
|
|
{
|
|
u64 hash = HashFromString(String8Struct(&key));
|
|
return HashTablePush(table, hash, (KeyValuePair){ .key_u64 = key, .value_rawptr = value });
|
|
}
|
|
|
|
static HashNode *HashTablePushU64U64Split(HashTable *table, u64 key, u32 upper, u32 lower)
|
|
{
|
|
u64 hash = HashFromString(String8Struct(&key));
|
|
return HashTablePush(table, hash, (KeyValuePair){ .key_u64 = key, .value_u64_split = { .upper = upper, .lower = lower }});
|
|
}
|
|
|
|
static HashNode *HashTablePushRawptrU64(HashTable *table, rawptr key, u64 value)
|
|
{
|
|
u64 hash = HashFromString(String8Struct(&key));
|
|
return HashTablePush(table, hash, (KeyValuePair){ .key_rawptr = key, .value_u64 = value });
|
|
}
|
|
|
|
static KeyValuePair *HashTableSearchU64(HashTable *table, u64 key)
|
|
{
|
|
KeyValuePair *result = NULL;
|
|
|
|
u64 hash = HashFromString(String8Struct(&key));
|
|
u64 index = hash % table->cap;
|
|
HashList *list = table->lists + index;
|
|
for (HashNode *node = list->first; node != P_HT_NIL; node = node->next)
|
|
{
|
|
if (node->v.key_u64 == key)
|
|
{
|
|
result = &node->v;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static KeyValuePair *HashTableSearchRawptr(HashTable *table, rawptr key)
|
|
{
|
|
KeyValuePair *result = NULL;
|
|
|
|
u64 hash = HashFromString(String8Struct(&key));
|
|
u64 index = hash % table->cap;
|
|
HashList *list = table->lists + index;
|
|
for (HashNode *node = list->first; node != P_HT_NIL; node = node->next)
|
|
{
|
|
if (node->v.key_rawptr == key)
|
|
{
|
|
result = &node->v;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void HashTableDeleteU64(HashTable *table, u64 key)
|
|
{
|
|
u64 hash = HashFromString(String8Struct(&key));
|
|
u64 index = hash % table->cap;
|
|
HashList *list = table->lists + index;
|
|
HashNode *prev = P_HT_NIL;
|
|
for (HashNode *node = list->first; node != P_HT_NIL; node = node->next)
|
|
{
|
|
if (node->v.key_u64 == key)
|
|
{
|
|
if (prev != P_HT_NIL)
|
|
prev->next = node->next;
|
|
|
|
node->v.key_u64 = 0;
|
|
node->v.value_u64 = 0;
|
|
HTQueuePush(table->free_lists.first, table->free_lists.last, node);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static rawptr HashTableDeleteU64Rawptr(HashTable *table, u64 key)
|
|
{
|
|
rawptr value = NULL;
|
|
|
|
u64 hash = HashFromString(String8Struct(&key));
|
|
u64 index = hash % table->cap;
|
|
HashList *list = table->lists + index;
|
|
HashNode *prev = P_HT_NIL;
|
|
for (HashNode *node = list->first; node != P_HT_NIL; node = node->next)
|
|
{
|
|
if (node->v.key_u64 == key)
|
|
{
|
|
if (prev != P_HT_NIL)
|
|
prev->next = node->next;
|
|
|
|
value = node->v.value_rawptr;
|
|
|
|
node->v.key_u64 = 0;
|
|
node->v.value_rawptr = NULL;
|
|
HTQueuePush(table->free_lists.first, table->free_lists.last, node);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
static U64Split HashTableDeleteU64U64Split(HashTable *table, u64 key)
|
|
{
|
|
U64Split value = { .upper = UINT32_MAX };
|
|
|
|
u64 hash = HashFromString(String8Struct(&key));
|
|
u64 index = hash % table->cap;
|
|
HashList *list = table->lists + index;
|
|
HashNode *prev = P_HT_NIL;
|
|
for (HashNode *node = list->first; node != P_HT_NIL; node = node->next)
|
|
{
|
|
if (node->v.key_u64 == key)
|
|
{
|
|
if (prev != P_HT_NIL)
|
|
prev->next = node->next;
|
|
|
|
value.upper = node->v.value_u64_split.upper;
|
|
value.lower = node->v.value_u64_split.lower;
|
|
|
|
node->v.key_u64 = 0;
|
|
node->v.value_u64_split.upper = 0;
|
|
node->v.value_u64_split.lower = 0;
|
|
HTQueuePush(table->free_lists.first, table->free_lists.last, node);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
// ::DataStructures::HashTable::Functions::End::
|
|
|
|
|