|
|
@ -8,28 +8,28 @@ |
|
|
|
#include <stdio.h> |
|
|
|
|
|
|
|
int debug_tree_black_height(TreeAlloc *node) { |
|
|
|
if (node == NULL) { |
|
|
|
return 1; |
|
|
|
} |
|
|
|
return ((node->color == COLOR_BLACK) ? 1 : 0) + debug_tree_black_height(node->left); |
|
|
|
if (node == NULL) { |
|
|
|
return 1; |
|
|
|
} |
|
|
|
return ((node->color == COLOR_BLACK) ? 1 : 0) + debug_tree_black_height(node->left); |
|
|
|
} |
|
|
|
|
|
|
|
void debug_print_tree(int indent, void *p) { |
|
|
|
TreeAlloc *node = (TreeAlloc*) p; |
|
|
|
if (node != NULL) { |
|
|
|
int bad = debug_tree_black_height(node->left) != debug_tree_black_height(node->right); |
|
|
|
bad |= node->color == COLOR_RED && ((node->left != NULL && node->left->color == COLOR_RED) || |
|
|
|
(node->right != NULL && node->right->color == COLOR_RED) || (node->parent != NULL && |
|
|
|
node->parent->color == COLOR_RED)); |
|
|
|
debug_print_tree(indent + 1, node->left); |
|
|
|
for (int ii = 0; ii < indent; ii++) { printf(" "); } |
|
|
|
if (node->color == COLOR_RED) { printf(bad ? "\e[30m]" : "\e[31m"); } |
|
|
|
if (bad) { printf("\e[43m"); } |
|
|
|
printf("%p %lu\n", node, node->size); |
|
|
|
if (node->color == COLOR_RED) { printf("\e[37m"); } |
|
|
|
if (bad) { printf("\e[40m"); } |
|
|
|
debug_print_tree(indent + 1, node->right); |
|
|
|
} |
|
|
|
TreeAlloc *node = (TreeAlloc*) p; |
|
|
|
if (node != NULL) { |
|
|
|
int bad = debug_tree_black_height(node->left) != debug_tree_black_height(node->right); |
|
|
|
bad |= node->color == COLOR_RED && ((node->left != NULL && node->left->color == COLOR_RED) || |
|
|
|
(node->right != NULL && node->right->color == COLOR_RED) || (node->parent != NULL && |
|
|
|
node->parent->color == COLOR_RED)); |
|
|
|
debug_print_tree(indent + 1, node->left); |
|
|
|
for (int ii = 0; ii < indent; ii++) { printf(" "); } |
|
|
|
if (node->color == COLOR_RED) { printf(bad ? "\e[30m]" : "\e[31m"); } |
|
|
|
if (bad) { printf("\e[43m"); } |
|
|
|
printf("%p %lu\n", node, node->size); |
|
|
|
if (node->color == COLOR_RED) { printf("\e[37m"); } |
|
|
|
if (bad) { printf("\e[40m"); } |
|
|
|
debug_print_tree(indent + 1, node->right); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#endif |
|
|
@ -42,26 +42,26 @@ TreeAlloc *insert_node_at(void *address, uintptr_t padding, uintptr_t align, uin |
|
|
|
* Search for the node whose allocated region contains an address. |
|
|
|
*/ |
|
|
|
TreeAlloc *search_by_address(TreeAlloc *root, void *address) { |
|
|
|
TreeAlloc *head = root; |
|
|
|
while (1) { |
|
|
|
if (head > (TreeAlloc*) address) { |
|
|
|
if (head->left == NULL) { |
|
|
|
return NULL; |
|
|
|
} else { |
|
|
|
head = head->left; |
|
|
|
} |
|
|
|
} else { |
|
|
|
if (head->right == NULL || head->right > (TreeAlloc*) address) { |
|
|
|
return head; |
|
|
|
} else { |
|
|
|
head = head->right; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
TreeAlloc *head = root; |
|
|
|
while (1) { |
|
|
|
if (head > (TreeAlloc*) address) { |
|
|
|
if (head->left == NULL) { |
|
|
|
return NULL; |
|
|
|
} else { |
|
|
|
head = head->left; |
|
|
|
} |
|
|
|
} else { |
|
|
|
if (head->right == NULL || head->right > (TreeAlloc*) address) { |
|
|
|
return head; |
|
|
|
} else { |
|
|
|
head = head->right; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static uintptr_t effective_size(TreeAlloc *head, uintptr_t padding, uintptr_t align) { |
|
|
|
return head->size - (align_after(head + padding, align) - (void*) head); |
|
|
|
return head->size - (align_after(head + padding, align) - (void*) head); |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
@ -70,55 +70,55 @@ static uintptr_t effective_size(TreeAlloc *head, uintptr_t padding, uintptr_t al |
|
|
|
* ordering. |
|
|
|
*/ |
|
|
|
static uintptr_t pessimistic_size(TreeAlloc *head, uintptr_t padding, uintptr_t align) { |
|
|
|
return head->size - padding - align + 1; |
|
|
|
return head->size - padding - align + 1; |
|
|
|
} |
|
|
|
|
|
|
|
TreeAlloc *search_by_size(TreeAlloc *root, uintptr_t padding, uintptr_t align, uintptr_t size) { |
|
|
|
TreeAlloc *head = root; |
|
|
|
while (1) { |
|
|
|
uintptr_t esize = pessimistic_size(head, padding, align); |
|
|
|
if (esize < size) { |
|
|
|
if (head->right == NULL) { |
|
|
|
return NULL; |
|
|
|
} else { |
|
|
|
head = head->right; |
|
|
|
} |
|
|
|
} else { |
|
|
|
if (head->left == NULL || pessimistic_size(head->left, padding, align) < size) { |
|
|
|
return head; |
|
|
|
} else { |
|
|
|
head = head->left; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
TreeAlloc *head = root; |
|
|
|
while (1) { |
|
|
|
uintptr_t esize = pessimistic_size(head, padding, align); |
|
|
|
if (esize < size) { |
|
|
|
if (head->right == NULL) { |
|
|
|
return NULL; |
|
|
|
} else { |
|
|
|
head = head->right; |
|
|
|
} |
|
|
|
} else { |
|
|
|
if (head->left == NULL || pessimistic_size(head->left, padding, align) < size) { |
|
|
|
return head; |
|
|
|
} else { |
|
|
|
head = head->left; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
TreeAlloc *succ(TreeAlloc *el) { |
|
|
|
if (el->right != NULL) { |
|
|
|
el = el->right; |
|
|
|
while (el->left != NULL) { |
|
|
|
el = el->left; |
|
|
|
} |
|
|
|
return el; |
|
|
|
} |
|
|
|
while (el->parent != NULL && el == el->parent->right) { |
|
|
|
el = el->parent; |
|
|
|
} |
|
|
|
return el->parent; |
|
|
|
if (el->right != NULL) { |
|
|
|
el = el->right; |
|
|
|
while (el->left != NULL) { |
|
|
|
el = el->left; |
|
|
|
} |
|
|
|
return el; |
|
|
|
} |
|
|
|
while (el->parent != NULL && el == el->parent->right) { |
|
|
|
el = el->parent; |
|
|
|
} |
|
|
|
return el->parent; |
|
|
|
} |
|
|
|
|
|
|
|
TreeAlloc *pred(TreeAlloc *el) { |
|
|
|
if (el->left != NULL) { |
|
|
|
el = el->left; |
|
|
|
while (el->right != NULL) { |
|
|
|
el = el->right; |
|
|
|
} |
|
|
|
return el; |
|
|
|
} |
|
|
|
while (el->parent != NULL && el == el->parent->left) { |
|
|
|
el = el->parent; |
|
|
|
} |
|
|
|
return el->parent; |
|
|
|
if (el->left != NULL) { |
|
|
|
el = el->left; |
|
|
|
while (el->right != NULL) { |
|
|
|
el = el->right; |
|
|
|
} |
|
|
|
return el; |
|
|
|
} |
|
|
|
while (el->parent != NULL && el == el->parent->left) { |
|
|
|
el = el->parent; |
|
|
|
} |
|
|
|
return el->parent; |
|
|
|
} |
|
|
|
|
|
|
|
TreeAlloc *get_sibling(TreeAlloc *ta) { |
|
|
@ -132,51 +132,47 @@ TreeAlloc *get_sibling(TreeAlloc *ta) { |
|
|
|
} |
|
|
|
|
|
|
|
void rotate_left(TreeAlloc **root_ptr, TreeAlloc *ta) { |
|
|
|
TreeAlloc *parent, *tmp; |
|
|
|
|
|
|
|
parent = ta->parent; |
|
|
|
tmp = ta->right; |
|
|
|
if (!tmp) return; |
|
|
|
|
|
|
|
ta->right = tmp->left; |
|
|
|
tmp->left = ta; |
|
|
|
ta->parent = tmp; |
|
|
|
|
|
|
|
if (ta->right) ta->right->parent = ta; |
|
|
|
|
|
|
|
if (parent == NULL) { |
|
|
|
*root_ptr = tmp; |
|
|
|
} else { |
|
|
|
if (ta == parent->left) |
|
|
|
parent->left = tmp; |
|
|
|
else |
|
|
|
parent->right = tmp; |
|
|
|
} |
|
|
|
tmp->parent = parent; |
|
|
|
TreeAlloc *parent, *tmp; |
|
|
|
|
|
|
|
tmp = ta->right; |
|
|
|
parent = ta->parent; |
|
|
|
|
|
|
|
ta->right = tmp->left; |
|
|
|
if (ta->right) ta->right->parent = ta; |
|
|
|
|
|
|
|
tmp->left = ta; |
|
|
|
ta->parent = tmp; |
|
|
|
tmp->parent = parent; |
|
|
|
|
|
|
|
if (!parent) { |
|
|
|
*root_ptr = tmp; |
|
|
|
} else if (ta == parent->left) { |
|
|
|
parent->left = tmp; |
|
|
|
} else { |
|
|
|
parent->right = tmp; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void rotate_right(TreeAlloc **root_ptr, TreeAlloc *ta) { |
|
|
|
TreeAlloc *parent, *tmp; |
|
|
|
|
|
|
|
parent = ta->parent; |
|
|
|
tmp = ta->left; |
|
|
|
if (!tmp) return; |
|
|
|
|
|
|
|
ta->left = tmp->right; |
|
|
|
tmp->right = ta; |
|
|
|
ta->parent = tmp; |
|
|
|
|
|
|
|
if (ta->left) ta->left->parent = ta; |
|
|
|
|
|
|
|
if (parent == NULL) { |
|
|
|
*root_ptr = tmp; |
|
|
|
} else { |
|
|
|
if (ta == parent->left) |
|
|
|
parent->left = tmp; |
|
|
|
else |
|
|
|
parent->right = tmp; |
|
|
|
} |
|
|
|
tmp->parent = parent; |
|
|
|
TreeAlloc *parent, *tmp; |
|
|
|
|
|
|
|
tmp = ta->left; |
|
|
|
parent = ta->parent; |
|
|
|
|
|
|
|
ta->left = tmp->right; |
|
|
|
if (ta->left) ta->left->parent = ta; |
|
|
|
|
|
|
|
tmp->right = ta; |
|
|
|
ta->parent = tmp; |
|
|
|
tmp->parent = parent; |
|
|
|
|
|
|
|
if (!parent) { |
|
|
|
*root_ptr = tmp; |
|
|
|
} else if (ta == parent->left) { |
|
|
|
parent->left = tmp; |
|
|
|
} else { |
|
|
|
parent->right = tmp; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#define IS_BLACK_NODE(n) (n == NULL || n->color == COLOR_BLACK) |
|
|
@ -185,51 +181,115 @@ void rotate_right(TreeAlloc **root_ptr, TreeAlloc *ta) { |
|
|
|
void repair_tree_after_insert(TreeAlloc **root_ptr, TreeAlloc *ta) { |
|
|
|
TreeAlloc *parent = ta->parent; |
|
|
|
#ifdef DEBUG |
|
|
|
printf("=== PRE-INSERT-FIXUP ===\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
printf("=== PRE-INSERT-FIXUP ===\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
#endif |
|
|
|
|
|
|
|
if (parent == NULL) { |
|
|
|
if (ta == *root_ptr) { |
|
|
|
ta->color = COLOR_BLACK; |
|
|
|
} else if (IS_BLACK_NODE(parent)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
TreeAlloc *grandparent = parent->parent; |
|
|
|
TreeAlloc *uncle = get_sibling(parent); |
|
|
|
} else { |
|
|
|
TreeAlloc *uncle = get_sibling(parent); |
|
|
|
TreeAlloc *grandparent = parent->parent; |
|
|
|
|
|
|
|
if (IS_RED_NODE(parent)) { |
|
|
|
if (IS_RED_NODE(uncle)) { |
|
|
|
parent->color = COLOR_BLACK; |
|
|
|
uncle->color = COLOR_BLACK; |
|
|
|
grandparent->color = COLOR_RED; |
|
|
|
grandparent->color = COLOR_RED; |
|
|
|
repair_tree_after_insert(root_ptr, grandparent); |
|
|
|
} else { |
|
|
|
if (ta == parent->left && parent == grandparent->left) { |
|
|
|
rotate_left(root_ptr, parent); |
|
|
|
ta = ta->left; |
|
|
|
parent = parent->left; |
|
|
|
if (parent->left == ta) { |
|
|
|
if (grandparent->left == parent) { |
|
|
|
rotate_right(root_ptr, grandparent); |
|
|
|
grandparent->color = COLOR_RED; |
|
|
|
parent->color = COLOR_BLACK; |
|
|
|
} else { |
|
|
|
rotate_right(root_ptr, parent); |
|
|
|
rotate_left(root_ptr, grandparent); |
|
|
|
grandparent->color = COLOR_RED; |
|
|
|
ta->color = COLOR_BLACK; |
|
|
|
} |
|
|
|
} else { |
|
|
|
rotate_right(root_ptr, parent); |
|
|
|
ta = ta->right; |
|
|
|
parent = parent->right; |
|
|
|
if (grandparent->left == parent) { |
|
|
|
rotate_left(root_ptr, parent); |
|
|
|
rotate_right(root_ptr, grandparent); |
|
|
|
grandparent->color = COLOR_RED; |
|
|
|
ta->color = COLOR_BLACK; |
|
|
|
} else { |
|
|
|
rotate_left(root_ptr, grandparent); |
|
|
|
grandparent->color = COLOR_RED; |
|
|
|
parent->color = COLOR_BLACK; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
grandparent = parent->parent; |
|
|
|
if (ta == parent->left) { |
|
|
|
rotate_right(root_ptr, grandparent); |
|
|
|
} else { |
|
|
|
rotate_left(root_ptr, grandparent); |
|
|
|
} |
|
|
|
parent->color = COLOR_BLACK; |
|
|
|
grandparent->color = COLOR_RED; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
// Inserts a node into an empty tree. |
|
|
|
void insert_singleton(TreeAlloc **root_ptr, TreeAlloc *to_insert) { |
|
|
|
#ifdef DEBUG |
|
|
|
printf("= PRE-INSERT-SINGLETON =\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
#endif |
|
|
|
*root_ptr = to_insert; |
|
|
|
to_insert->parent = NULL; |
|
|
|
to_insert->color = COLOR_BLACK; |
|
|
|
#ifdef DEBUG |
|
|
|
printf("= POST-INSERT-SINGLETON =\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
void insert_right(TreeAlloc** root_ptr, TreeAlloc* to_insert, TreeAlloc* after) { |
|
|
|
#ifdef DEBUG |
|
|
|
printf("=== PRE-INSERT-RIGHT ===\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
#endif |
|
|
|
if (after->right != NULL) { |
|
|
|
after->right->parent = to_insert; |
|
|
|
to_insert->right = after->right; |
|
|
|
} |
|
|
|
after->right = to_insert; |
|
|
|
to_insert->parent = after; |
|
|
|
to_insert->color = COLOR_RED; |
|
|
|
repair_tree_after_insert(root_ptr, to_insert); |
|
|
|
#ifdef DEBUG |
|
|
|
printf("== POST-INSERT-FIXUP ===\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
void insert_left(TreeAlloc** root_ptr, TreeAlloc* to_insert, TreeAlloc* before) { |
|
|
|
#ifdef DEBUG |
|
|
|
printf("=== PRE-INSERT-LEFT ====\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
#endif |
|
|
|
if (before->left != NULL) { |
|
|
|
before->left->parent = to_insert; |
|
|
|
to_insert->left = before->left; |
|
|
|
} |
|
|
|
before->left = to_insert; |
|
|
|
to_insert->parent = before; |
|
|
|
to_insert->color = COLOR_RED; |
|
|
|
repair_tree_after_insert(root_ptr, to_insert); |
|
|
|
#ifdef DEBUG |
|
|
|
printf("== POST-INSERT-FIXUP ===\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
printf("== POST-INSERT-FIXUP ===\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
@ -291,10 +351,10 @@ void repair_after_remove(TreeAlloc **root_ptr, TreeAlloc *node) { |
|
|
|
void remove_node(TreeAlloc **root_ptr, TreeAlloc *node) { |
|
|
|
char do_repair = 0; |
|
|
|
#ifdef DEBUG |
|
|
|
printf("====== PRE-REMOVE ======\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
printf("====== PRE-REMOVE ======\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
#endif |
|
|
|
TreeAlloc *replace; |
|
|
|
TreeAlloc *parent = node->parent; |
|
|
@ -322,295 +382,241 @@ void remove_node(TreeAlloc **root_ptr, TreeAlloc *node) { |
|
|
|
node->left->parent = tmp; |
|
|
|
} |
|
|
|
|
|
|
|
// Make sure that it doesn't have any tree pointers it shouldn't have. |
|
|
|
node->parent = node->left = node->right = NULL; |
|
|
|
// Make sure that it doesn't have any tree pointers it shouldn't have. |
|
|
|
node->parent = node->left = node->right = NULL; |
|
|
|
|
|
|
|
if (replace && replace->parent == NULL) { |
|
|
|
#ifdef DEBUG |
|
|
|
printf("=== PRE-REMOVE-FIXUP ===\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
printf("=== PRE-REMOVE-FIXUP ===\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
#endif |
|
|
|
replace->color = COLOR_BLACK; |
|
|
|
replace->color = COLOR_BLACK; |
|
|
|
} else if (do_repair && replace) { |
|
|
|
#ifdef DEBUG |
|
|
|
printf("=== PRE-REMOVE-FIXUP ===\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
printf("=== PRE-REMOVE-FIXUP ===\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
#endif |
|
|
|
repair_after_remove(root_ptr, replace); |
|
|
|
} |
|
|
|
#ifdef DEBUG |
|
|
|
printf("=== POST-REMOVE ===\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Inserts a node into an empty tree. |
|
|
|
void insert_singleton(TreeAlloc **root_ptr, TreeAlloc *to_insert) { |
|
|
|
#ifdef DEBUG |
|
|
|
printf("= PRE-INSERT-SINGLETON =\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
#endif |
|
|
|
*root_ptr = to_insert; |
|
|
|
to_insert->parent = NULL; |
|
|
|
to_insert->color = COLOR_BLACK; |
|
|
|
#ifdef DEBUG |
|
|
|
printf("= POST-INSERT-SINGLETON =\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
void insert_right(TreeAlloc** root_ptr, TreeAlloc* to_insert, TreeAlloc* after) { |
|
|
|
#ifdef DEBUG |
|
|
|
printf("=== PRE-INSERT-RIGHT ===\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
printf("=== POST-REMOVE ===\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
#endif |
|
|
|
if (after->right != NULL) { |
|
|
|
after->right->parent = to_insert; |
|
|
|
to_insert->right = after->right; |
|
|
|
} |
|
|
|
after->right = to_insert; |
|
|
|
to_insert->parent = after; |
|
|
|
to_insert->color = COLOR_RED; |
|
|
|
repair_tree_after_insert(root_ptr, to_insert); |
|
|
|
} |
|
|
|
|
|
|
|
void insert_left(TreeAlloc** root_ptr, TreeAlloc* to_insert, TreeAlloc* before) { |
|
|
|
#ifdef DEBUG |
|
|
|
printf("=== PRE-INSERT-LEFT ====\n"); |
|
|
|
printf("===== CURRENT TREE =====\n"); |
|
|
|
debug_print_tree(0, *root_ptr); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
#endif |
|
|
|
if (before->left != NULL) { |
|
|
|
before->left->parent = to_insert; |
|
|
|
to_insert->left = before->left; |
|
|
|
} |
|
|
|
before->left = to_insert; |
|
|
|
to_insert->parent = before; |
|
|
|
to_insert->color = COLOR_RED; |
|
|
|
repair_tree_after_insert(root_ptr, to_insert); |
|
|
|
} |
|
|
|
|
|
|
|
int add_new_region(Arena *arena, uintptr_t size, uintptr_t padding, uintptr_t align) { |
|
|
|
uintptr_t realsize = size + align + alignof(WatermarkAlloc) + padding - 1; |
|
|
|
if (realsize < MIN_NEW_MEM_SIZE) { |
|
|
|
realsize = MIN_NEW_MEM_SIZE; |
|
|
|
} |
|
|
|
FreeSpace *reg = (FreeSpace*) arena->get_new_region(realsize); |
|
|
|
if (reg == NULL) { |
|
|
|
arena->error("can't allocate a new memory region!"); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
FreeSpace *newreg = align_after(reg, alignof(WatermarkAlloc)); |
|
|
|
newreg->left = NULL; |
|
|
|
newreg->right = NULL; |
|
|
|
realsize -= (void*) newreg - (void*) reg; |
|
|
|
realsize -= realsize % alignof(WatermarkAlloc); |
|
|
|
newreg->size = realsize; |
|
|
|
if (arena->root_freespace == NULL) { |
|
|
|
insert_singleton((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) newreg); |
|
|
|
} else { |
|
|
|
FreeSpace *head = arena->root_freespace; |
|
|
|
while (head->right != NULL) { |
|
|
|
head = head->right; |
|
|
|
} |
|
|
|
insert_right((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) newreg, (TreeAlloc*) head); |
|
|
|
} |
|
|
|
uintptr_t realsize = size + align + alignof(WatermarkAlloc) + padding - 1; |
|
|
|
if (realsize < MIN_NEW_MEM_SIZE) { |
|
|
|
realsize = MIN_NEW_MEM_SIZE; |
|
|
|
} |
|
|
|
FreeSpace *reg = (FreeSpace*) arena->get_new_region(realsize); |
|
|
|
if (reg == NULL) { |
|
|
|
arena->error("can't allocate a new memory region!"); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
FreeSpace *newreg = align_after(reg, alignof(WatermarkAlloc)); |
|
|
|
newreg->left = NULL; |
|
|
|
newreg->right = NULL; |
|
|
|
realsize -= (void*) newreg - (void*) reg; |
|
|
|
realsize -= realsize % alignof(WatermarkAlloc); |
|
|
|
newreg->size = realsize; |
|
|
|
if (arena->root_freespace == NULL) { |
|
|
|
insert_singleton((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) newreg); |
|
|
|
} else { |
|
|
|
FreeSpace *head = arena->root_freespace; |
|
|
|
while (head->right != NULL) { |
|
|
|
head = head->right; |
|
|
|
} |
|
|
|
insert_right((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) newreg, (TreeAlloc*) head); |
|
|
|
} |
|
|
|
#ifdef DEBUG |
|
|
|
printf("= POST-REGION-CREATION =\n"); |
|
|
|
printf("==== FREESPACE TREE ====\n"); |
|
|
|
debug_print_tree(0, arena->root_freespace); |
|
|
|
printf("==== TREEALLOC TREE ====\n"); |
|
|
|
debug_print_tree(0, arena->root_treealloc); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
printf("= POST-REGION-CREATION =\n"); |
|
|
|
printf("==== FREESPACE TREE ====\n"); |
|
|
|
debug_print_tree(0, arena->root_freespace); |
|
|
|
printf("==== TREEALLOC TREE ====\n"); |
|
|
|
debug_print_tree(0, arena->root_treealloc); |
|
|
|
printf("===== END OF TREES =====\n"); |
|
|
|
#endif |
|
|
|
return 1; |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
void unalloc(Arena *arena, void *addr) { |
|
|
|
#ifdef DEBUG |
|
|
|
printf("==== UNALLOCATING ====\n"); |
|
|
|
printf("=== FREESPACE TREE ===\n"); |
|
|
|
debug_print_tree(0, arena->root_freespace); |
|
|
|
printf("=== TREEALLOC TREE ===\n"); |
|
|
|
debug_print_tree(0, arena->root_treealloc); |
|
|
|
printf("==== END OF TREES ====\n"); |
|
|
|
printf("==== UNALLOCATING ====\n"); |
|
|
|
printf("=== FREESPACE TREE ===\n"); |
|
|
|
debug_print_tree(0, arena->root_freespace); |
|
|
|
printf("=== TREEALLOC TREE ===\n"); |
|
|
|
debug_print_tree(0, arena->root_treealloc); |
|
|
|
printf("==== END OF TREES ====\n"); |
|
|
|
#endif |
|
|
|
if (arena->root_treealloc == NULL) { |
|
|
|
arena->error("attempt to unallocate when there are no allocations!"); |
|
|
|
return; |
|
|
|
} |
|
|
|
// Find the node this address belongs to |
|
|
|
TreeAlloc *node = search_by_address(arena->root_treealloc, addr); |
|
|
|
if (node == NULL) { |
|
|
|
arena->error("attempt to free memory outside any allocations!"); |
|
|
|
return; |
|
|
|
} |
|
|
|
// Handle the watermark allocator in this region |
|
|
|
if (node->type == RT_WATERMARK) { |
|
|
|
// TODO: handle watermark deallocation |
|
|
|
return; |
|
|
|
} |
|
|
|
// Get rid of it |
|
|
|
remove_node(&arena->root_treealloc, node); |
|
|
|
// If there's free space on either side of it, merge it with the free space into a bigger chunk of |
|
|
|
// free space. |
|
|
|
uintptr_t size = node->size; |
|
|
|
FreeSpace *start = (FreeSpace*) node; |
|
|
|
if (node->before != NULL && node->before->type == RT_FREESPACE) { |
|
|
|
start = (FreeSpace*) node->before; |
|
|
|
size += node->before->size; |
|
|
|
remove_node((TreeAlloc**) &arena->root_freespace, node->before); |
|
|
|
} |
|
|
|
if (node->after != NULL && node->after->type == RT_FREESPACE) { |
|
|
|
size += node->after->size; |
|
|
|
remove_node((TreeAlloc**) &arena->root_freespace, node->after); |
|
|
|
} |
|
|
|
start->type = RT_FREESPACE; |
|
|
|
start->size = size; |
|
|
|
// And finally, insert the resulting free space. |
|
|
|
if (arena->root_freespace == NULL) { |
|
|
|
insert_singleton((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) start); |
|
|
|
} else { |
|
|
|
TreeAlloc *insert_point = search_by_size((TreeAlloc*) arena->root_freespace, 0, 1, size); |
|
|
|
if (insert_point == NULL) { |
|
|
|
TreeAlloc *head = (TreeAlloc*) arena->root_freespace; |
|
|
|
while (head->right != NULL) { |
|
|
|
head = head->right; |
|
|
|
} |
|
|
|
insert_right((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) start, head); |
|
|
|
} else { |
|
|
|
insert_left((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) start, insert_point); |
|
|
|
} |
|
|
|
} |
|
|
|
if (arena->root_treealloc == NULL) { |
|
|
|
arena->error("attempt to unallocate when there are no allocations!"); |
|
|
|
return; |
|
|
|
} |
|
|
|
// Find the node this address belongs to |
|
|
|
TreeAlloc *node = search_by_address(arena->root_treealloc, addr); |
|
|
|
if (node == NULL) { |
|
|
|
arena->error("attempt to free memory outside any allocations!"); |
|
|
|
return; |
|
|
|
} |
|
|
|
// Handle the watermark allocator in this region |
|
|
|
if (node->type == RT_WATERMARK) { |
|
|
|
// TODO: handle watermark deallocation |
|
|
|
return; |
|
|
|
} |
|
|
|
// Get rid of it |
|
|
|
remove_node(&arena->root_treealloc, node); |
|
|
|
// If there's free space on either side of it, merge it with the free space into a bigger chunk of |
|
|
|
// free space. |
|
|
|
uintptr_t size = node->size; |
|
|
|
FreeSpace *start = (FreeSpace*) node; |
|
|
|
if (node->before != NULL && node->before->type == RT_FREESPACE) { |
|
|
|
start = (FreeSpace*) node->before; |
|
|
|
size += node->before->size; |
|
|
|
remove_node((TreeAlloc**) &arena->root_freespace, node->before); |
|
|
|
} |
|
|
|
if (node->after != NULL && node->after->type == RT_FREESPACE) { |
|
|
|
size += node->after->size; |
|
|
|
remove_node((TreeAlloc**) &arena->root_freespace, node->after); |
|
|
|
} |
|
|
|
start->type = RT_FREESPACE; |
|
|
|
start->size = size; |
|
|
|
// And finally, insert the resulting free space. |
|
|
|
if (arena->root_freespace == NULL) { |
|
|
|
insert_singleton((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) start); |
|
|
|
} else { |
|
|
|
TreeAlloc *insert_point = search_by_size((TreeAlloc*) arena->root_freespace, 0, 1, size); |
|
|
|
if (insert_point == NULL) { |
|
|
|
TreeAlloc *head = (TreeAlloc*) arena->root_freespace; |
|
|
|
while (head->right != NULL) { |
|
|
|
head = head->right; |
|
|
|
} |
|
|
|
insert_right((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) start, head); |
|
|
|
} else { |
|
|
|
insert_left((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) start, insert_point); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void *alloc(Arena *arena, uintptr_t size, uintptr_t align) { |
|
|
|
uintptr_t actual_align = lcm(alignof(struct WatermarkAlloc), align); |
|
|
|
uintptr_t actual_align = lcm(alignof(struct WatermarkAlloc), align); |
|
|
|
#ifdef DEBUG |
|
|
|
printf("==== ALLOCATING =====\n"); |
|
|
|
printf("=== FREESPACE TREE ===\n"); |
|
|
|
debug_print_tree(0, arena->root_freespace); |
|
|
|
printf("=== TREEALLOC TREE ===\n"); |
|
|
|
debug_print_tree(0, arena->root_treealloc); |
|
|
|
printf("==== END OF TREES ====\n"); |
|
|
|
printf("==== ALLOCATING =====\n"); |
|
|
|
printf("=== FREESPACE TREE ===\n"); |
|
|
|
debug_print_tree(0, arena->root_freespace); |
|
|
|
printf("=== TREEALLOC TREE ===\n"); |
|
|
|
debug_print_tree(0, arena->root_treealloc); |
|
|
|
printf("==== END OF TREES ====\n"); |
|
|
|
#endif |
|
|
|
if (arena->root_freespace == NULL) { |
|
|
|
// Handle being out of freespace. |
|
|
|
if (arena->root_freespace == NULL) { |
|
|
|
// Handle being out of freespace. |
|
|
|
#ifdef DEBUG |
|
|
|
printf("Out of freespace nodes; getting more\n"); |
|
|
|
printf("Out of freespace nodes; getting more\n"); |
|
|
|
#endif |
|
|
|
if (!add_new_region(arena, size, sizeof(TreeAlloc), actual_align)) { |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
return alloc(arena, size, align); |
|
|
|
} else { |
|
|
|
TreeAlloc *region = search_by_size((TreeAlloc*) arena->root_freespace, sizeof(TreeAlloc), actual_align, size); |
|
|
|
if (region == NULL) { |
|
|
|
// Handle insufficient freespace or fragmentation. |
|
|
|
if (!add_new_region(arena, size, sizeof(TreeAlloc), actual_align)) { |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
return alloc(arena, size, align); |
|
|
|
} else { |
|
|
|
TreeAlloc *region = search_by_size((TreeAlloc*) arena->root_freespace, sizeof(TreeAlloc), actual_align, size); |
|
|
|
if (region == NULL) { |
|
|
|
// Handle insufficient freespace or fragmentation. |
|
|
|
#ifdef DEBUG |
|
|
|
printf("Out of sufficiently large freespace nodes; getting more\n"); |
|
|
|
printf("Out of sufficiently large freespace nodes; getting more\n"); |
|
|
|
#endif |
|
|
|
if (!add_new_region(arena, size, sizeof(TreeAlloc), actual_align)) { |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
return alloc(arena, size, align); |
|
|
|
} |
|
|
|
remove_node((TreeAlloc**) &arena->root_freespace, region); |
|
|
|
void *true_end = align_after(align_after(((void*) region) + sizeof(TreeAlloc), actual_align) + size, alignof(WatermarkAlloc)); |
|
|
|
// The size of the new allocation (adjusted for region header and alignment |
|
|
|
uintptr_t new_size = true_end - (void*) region; |
|
|
|
// The size of the free space region following the new allocation |
|
|
|
uintptr_t new_free_size = region->size - new_size; |
|
|
|
region->right = NULL; |
|
|
|
region->left = NULL; |
|
|
|
region->type = RT_TREE_NODE; |
|
|
|
if (!add_new_region(arena, size, sizeof(TreeAlloc), actual_align)) { |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
return alloc(arena, size, align); |
|
|
|
} |
|
|
|
remove_node((TreeAlloc**) &arena->root_freespace, region); |
|
|
|
void *true_end = align_after(align_after(((void*) region) + sizeof(TreeAlloc), actual_align) + size, alignof(WatermarkAlloc)); |
|
|
|
// The size of the new allocation (adjusted for region header and alignment |
|
|
|
uintptr_t new_size = true_end - (void*) region; |
|
|
|
// The size of the free space region following the new allocation |
|
|
|
uintptr_t new_free_size = region->size - new_size; |
|
|
|
region->right = NULL; |
|
|
|
region->left = NULL; |
|
|
|
region->type = RT_TREE_NODE; |
|
|
|
#ifdef DEBUG |
|
|
|
printf("start: %p, end: %p, adjusted end: %p\n", region, ((void*) region) + size, true_end); |
|
|
|
printf("size: %lu -> %lu\n", size, new_size); |
|
|
|
printf("start: %p, end: %p, adjusted end: %p\n", region, ((void*) region) + size, true_end); |
|
|
|
printf("size: %lu -> %lu\n", size, new_size); |
|
|
|
#endif |
|
|
|
if (arena->root_treealloc == NULL) { |
|
|
|
insert_singleton((TreeAlloc**) &arena->root_treealloc, region); |
|
|
|
} else { |
|
|
|
if (arena->root_treealloc == NULL) { |
|
|
|
insert_singleton((TreeAlloc**) &arena->root_treealloc, region); |
|
|
|
} else { |
|
|
|
#ifdef DEBUG |
|
|
|
printf("searching for an insert point\n"); |
|
|
|
printf("searching for an insert point\n"); |
|
|
|
#endif |
|
|
|
TreeAlloc *insert_point = search_by_address((TreeAlloc*) arena->root_treealloc, region); |
|
|
|
if (insert_point == NULL) { |
|
|
|
TreeAlloc *head = arena->root_treealloc; |
|
|
|
while (head->left != NULL) { |
|
|
|
head = head->left; |
|
|
|
} |
|
|
|
TreeAlloc *insert_point = search_by_address((TreeAlloc*) arena->root_treealloc, region); |
|
|
|
if (insert_point == NULL) { |
|
|
|
TreeAlloc *head = arena->root_treealloc; |
|
|
|
while (head->left != NULL) { |
|
|
|
head = head->left; |
|
|
|
} |
|
|
|
#ifdef DEBUG |
|
|
|
printf("none found; inserting before %p\n", head); |
|
|
|
printf("none found; inserting before %p\n", head); |
|
|
|
#endif |
|
|
|
insert_left(&arena->root_treealloc, region, head); |
|
|
|
} else { |
|
|
|
insert_left(&arena->root_treealloc, region, head); |
|
|
|
} else { |
|
|
|
#ifdef DEBUG |
|
|
|
printf("found one: %p\n", insert_point); |
|
|
|
printf("found one: %p\n", insert_point); |
|
|
|
#endif |
|
|
|
insert_right(&arena->root_treealloc, region, insert_point); |
|
|
|
} |
|
|
|
} |
|
|
|
if (new_free_size >= sizeof(FreeSpace)) { |
|
|
|
// If there's enough free space after the allocation, use it! |
|
|
|
region->size = new_size; // Safe because the allocated region tree is not sorted by size. |
|
|
|
FreeSpace *new_free = (FreeSpace*) ((void*) region + new_size); |
|
|
|
new_free->left = NULL; |
|
|
|
new_free->right = NULL; |
|
|
|
new_free->type = RT_FREESPACE; |
|
|
|
new_free->size = new_free_size; |
|
|
|
if (arena->root_freespace == NULL) { |
|
|
|
insert_singleton((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) new_free); |
|
|
|
} else { |
|
|
|
FreeSpace *insert_point = (FreeSpace*) search_by_size((TreeAlloc*) arena->root_freespace, 0, 1, new_free_size); |
|
|
|
insert_left((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) new_free, (TreeAlloc*) insert_point); |
|
|
|
} |
|
|
|
// Set the region following this one to be the new free space |
|
|
|
region->after = (TreeAlloc*) new_free; |
|
|
|
} else { |
|
|
|
// There isn't a free space after this one, so put the `next` pointer at the next allocated |
|
|
|
// region. |
|
|
|
region->after = succ(region); |
|
|
|
} |
|
|
|
// I seem to have forgotten about the fact that memory may not be contiguous |
|
|
|
if (region->after != NULL && region->after != (void*) region + region->size) { |
|
|
|
region->after = NULL; |
|
|
|
} |
|
|
|
// Also make sure the `before` pointer is correct. |
|
|
|
TreeAlloc *before_alloc = pred(region); |
|
|
|
if (before_alloc == NULL || ((void*) before_alloc) + before_alloc->size < (void*) region) { |
|
|
|
region->before = search_by_address((TreeAlloc*) arena->root_freespace, region); |
|
|
|
} else { |
|
|
|
region->before = before_alloc; |
|
|
|
} |
|
|
|
// I seem to have forgotten about the fact that memory may not be contiguous |
|
|
|
if (region->before != NULL && region->before != (void*) region->before + region->before->size) { |
|
|
|
region->before = NULL; |
|
|
|
} |
|
|
|
insert_right(&arena->root_treealloc, region, insert_point); |
|
|
|
} |
|
|
|
} |
|
|
|
if (new_free_size >= sizeof(FreeSpace)) { |
|
|
|
// If there's enough free space after the allocation, use it! |
|
|
|
region->size = new_size; // Safe because the allocated region tree is not sorted by size. |
|
|
|
FreeSpace *new_free = (FreeSpace*) ((void*) region + new_size); |
|
|
|
new_free->left = NULL; |
|
|
|
new_free->right = NULL; |
|
|
|
new_free->type = RT_FREESPACE; |
|
|
|
new_free->size = new_free_size; |
|
|
|
if (arena->root_freespace == NULL) { |
|
|
|
insert_singleton((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) new_free); |
|
|
|
} else { |
|
|
|
FreeSpace *insert_point = (FreeSpace*) search_by_size((TreeAlloc*) arena->root_freespace, 0, 1, new_free_size); |
|
|
|
insert_left((TreeAlloc**) &arena->root_freespace, (TreeAlloc*) new_free, (TreeAlloc*) insert_point); |
|
|
|
} |
|
|
|
// Set the region following this one to be the new free space |
|
|
|
region->after = (TreeAlloc*) new_free; |
|
|
|
} else { |
|
|
|
// There isn't a free space after this one, so put the `next` pointer at the next allocated |
|
|
|
// region. |
|
|
|
region->after = succ(region); |
|
|
|
} |
|
|
|
// I seem to have forgotten about the fact that memory may not be contiguous |
|
|
|
if (region->after != NULL && region->after != (void*) region + region->size) { |
|
|
|
region->after = NULL; |
|
|
|
} |
|
|
|
// Also make sure the `before` pointer is correct. |
|
|
|
TreeAlloc *before_alloc = pred(region); |
|
|
|
if (before_alloc == NULL || ((void*) before_alloc) + before_alloc->size < (void*) region) { |
|
|
|
region->before = search_by_address((TreeAlloc*) arena->root_freespace, region); |
|
|
|
} else { |
|
|
|
region->before = before_alloc; |
|
|
|
} |
|
|
|
// I seem to have forgotten about the fact that memory may not be contiguous |
|
|
|
if (region->before != NULL && region->before != (void*) region->before + region->before->size) { |
|
|
|
region->before = NULL; |
|
|
|
} |
|
|
|
#ifdef DEBUG |
|
|
|
printf("region is still at %p\n", region); |
|
|
|
printf("region is still at %p\n", region); |
|
|
|
#endif |
|
|
|
return align_after((void*) region + sizeof(TreeAlloc), actual_align); |
|
|
|
} |
|
|
|
return align_after((void*) region + sizeof(TreeAlloc), actual_align); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void *alloc_growable(Arena *arena, uintptr_t size, uintptr_t align) { |
|
|
|
// TODO: Basically the same as above, but put the allocated region in the center of the largest free |
|
|
|
// space. Due to alignment and whatnot, the code will be gory. |
|
|
|
return NULL; |
|
|
|
// TODO: Basically the same as above, but put the allocated region in the center of the largest free |
|
|
|
// space. Due to alignment and whatnot, the code will be gory. |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|