Reduce calls to malloc in multiset (#100)

Reduce the number of malloc calls in multiset. Improves efficiency by 30%.
This commit is contained in:
Bailey Thompson
2020-08-15 01:52:54 -04:00
committed by GitHub
parent aab2a074d5
commit ac725ef147
5 changed files with 373 additions and 277 deletions

View File

@@ -385,12 +385,12 @@ multiset multiset_init(size_t key_size,
const void *const two)); const void *const two));
/* Capacity */ /* Capacity */
int multiset_size(multiset me); size_t multiset_size(multiset me);
int multiset_is_empty(multiset me); int multiset_is_empty(multiset me);
/* Accessing */ /* Accessing */
int multiset_put(multiset me, void *key); int multiset_put(multiset me, void *key);
int multiset_count(multiset me, void *key); size_t multiset_count(multiset me, void *key);
int multiset_contains(multiset me, void *key); int multiset_contains(multiset me, void *key);
int multiset_remove(multiset me, void *key); int multiset_remove(multiset me, void *key);
int multiset_remove_all(multiset me, void *key); int multiset_remove_all(multiset me, void *key);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2017-2019 Bailey Thompson * Copyright (c) 2017-2020 Bailey Thompson
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -37,12 +37,12 @@ multiset multiset_init(size_t key_size,
const void *const two)); const void *const two));
/* Capacity */ /* Capacity */
int multiset_size(multiset me); size_t multiset_size(multiset me);
int multiset_is_empty(multiset me); int multiset_is_empty(multiset me);
/* Accessing */ /* Accessing */
int multiset_put(multiset me, void *key); int multiset_put(multiset me, void *key);
int multiset_count(multiset me, void *key); size_t multiset_count(multiset me, void *key);
int multiset_contains(multiset me, void *key); int multiset_contains(multiset me, void *key);
int multiset_remove(multiset me, void *key); int multiset_remove(multiset me, void *key);
int multiset_remove_all(multiset me, void *key); int multiset_remove_all(multiset me, void *key);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2017-2019 Bailey Thompson * Copyright (c) 2017-2020 Bailey Thompson
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,20 +25,22 @@
#include "include/multiset.h" #include "include/multiset.h"
struct internal_multiset { struct internal_multiset {
size_t size;
size_t key_size; size_t key_size;
int (*comparator)(const void *const one, const void *const two); int (*comparator)(const void *const one, const void *const two);
int size; char *root;
struct node *root;
}; };
struct node { static const size_t ptr_size = sizeof(char *);
int count; static const size_t count_size = sizeof(size_t);
struct node *parent; /* Node balance is always the first byte (at index 0). */
int balance; static const size_t node_count_offset = sizeof(signed char);
void *key; static const size_t node_parent_offset = 1 + sizeof(size_t);
struct node *left; static const size_t node_left_child_offset =
struct node *right; 1 + sizeof(size_t) + sizeof(char *);
}; static const size_t node_right_child_offset =
1 + sizeof(size_t) + 2 * sizeof(char *);
static const size_t node_key_offset = 1 + sizeof(size_t) + 3 * sizeof(char *);
/** /**
* Initializes a multi-set. * Initializes a multi-set.
@@ -64,9 +66,9 @@ multiset multiset_init(const size_t key_size,
if (!init) { if (!init) {
return NULL; return NULL;
} }
init->size = 0;
init->key_size = key_size; init->key_size = key_size;
init->comparator = comparator; init->comparator = comparator;
init->size = 0;
init->root = NULL; init->root = NULL;
return init; return init;
} }
@@ -78,7 +80,7 @@ multiset multiset_init(const size_t key_size,
* *
* @return the size of the multi-set * @return the size of the multi-set
*/ */
int multiset_size(multiset me) size_t multiset_size(multiset me)
{ {
return me->size; return me->size;
} }
@@ -98,70 +100,73 @@ int multiset_is_empty(multiset me)
/* /*
* Resets the parent reference. * Resets the parent reference.
*/ */
static void multiset_reference_parent(multiset me, static void multiset_reference_parent(multiset me, char *const parent,
struct node *const parent, char *const child)
struct node *const child)
{ {
child->parent = parent->parent; char *grand_parent;
if (!parent->parent) { char *grand_parent_left_child;
memcpy(child + node_parent_offset, parent + node_parent_offset, ptr_size);
memcpy(&grand_parent, parent + node_parent_offset, ptr_size);
if (!grand_parent) {
me->root = child; me->root = child;
} else if (parent->parent->left == parent) { return;
parent->parent->left = child; }
memcpy(&grand_parent_left_child, grand_parent + node_left_child_offset,
ptr_size);
if (grand_parent_left_child == parent) {
memcpy(grand_parent + node_left_child_offset, &child, ptr_size);
} else { } else {
parent->parent->right = child; memcpy(grand_parent + node_right_child_offset, &child, ptr_size);
} }
} }
/* /*
* Rotates the AVL tree to the left. * Rotates the AVL tree to the left.
*/ */
static void multiset_rotate_left(multiset me, static void multiset_rotate_left(multiset me, char *const parent,
struct node *const parent, char *const child)
struct node *const child)
{ {
struct node *grand_child; char *left_grand_child;
multiset_reference_parent(me, parent, child); multiset_reference_parent(me, parent, child);
grand_child = child->left; memcpy(&left_grand_child, child + node_left_child_offset, ptr_size);
if (grand_child) { if (left_grand_child) {
grand_child->parent = parent; memcpy(left_grand_child + node_parent_offset, &parent, ptr_size);
} }
parent->parent = child; memcpy(parent + node_parent_offset, &child, ptr_size);
parent->right = grand_child; memcpy(parent + node_right_child_offset, &left_grand_child, ptr_size);
child->left = parent; memcpy(child + node_left_child_offset, &parent, ptr_size);
} }
/* /*
* Rotates the AVL tree to the right. * Rotates the AVL tree to the right.
*/ */
static void multiset_rotate_right(multiset me, static void multiset_rotate_right(multiset me, char *const parent,
struct node *const parent, char *const child)
struct node *const child)
{ {
struct node *grand_child; char *right_grand_child;
multiset_reference_parent(me, parent, child); multiset_reference_parent(me, parent, child);
grand_child = child->right; memcpy(&right_grand_child, child + node_right_child_offset, ptr_size);
if (grand_child) { if (right_grand_child) {
grand_child->parent = parent; memcpy(right_grand_child + node_parent_offset, &parent, ptr_size);
} }
parent->parent = child; memcpy(parent + node_parent_offset, &child, ptr_size);
parent->left = grand_child; memcpy(parent + node_left_child_offset, &right_grand_child, ptr_size);
child->right = parent; memcpy(child + node_right_child_offset, &parent, ptr_size);
} }
/* /*
* Performs a left repair. * Performs a left repair.
*/ */
static struct node *multiset_repair_left(multiset me, static char *multiset_repair_left(multiset me, char *const parent,
struct node *const parent, char *const child)
struct node *const child)
{ {
multiset_rotate_left(me, parent, child); multiset_rotate_left(me, parent, child);
if (child->balance == 0) { if (child[0] == 0) {
parent->balance = 1; parent[0] = 1;
child->balance = -1; child[0] = -1;
} else { } else {
parent->balance = 0; parent[0] = 0;
child->balance = 0; child[0] = 0;
} }
return child; return child;
} }
@@ -169,17 +174,16 @@ static struct node *multiset_repair_left(multiset me,
/* /*
* Performs a right repair. * Performs a right repair.
*/ */
static struct node *multiset_repair_right(multiset me, static char *multiset_repair_right(multiset me, char *const parent,
struct node *const parent, char *const child)
struct node *const child)
{ {
multiset_rotate_right(me, parent, child); multiset_rotate_right(me, parent, child);
if (child->balance == 0) { if (child[0] == 0) {
parent->balance = -1; parent[0] = -1;
child->balance = 1; child[0] = 1;
} else { } else {
parent->balance = 0; parent[0] = 0;
child->balance = 0; child[0] = 0;
} }
return child; return child;
} }
@@ -187,48 +191,46 @@ static struct node *multiset_repair_right(multiset me,
/* /*
* Performs a left-right repair. * Performs a left-right repair.
*/ */
static struct node *multiset_repair_left_right(multiset me, static char *multiset_repair_left_right(multiset me, char *const parent,
struct node *const parent, char *const child,
struct node *const child, char *const grand_child)
struct node *const grand_child)
{ {
multiset_rotate_left(me, child, grand_child); multiset_rotate_left(me, child, grand_child);
multiset_rotate_right(me, parent, grand_child); multiset_rotate_right(me, parent, grand_child);
if (grand_child->balance == 1) { if (grand_child[0] == 1) {
parent->balance = 0; parent[0] = 0;
child->balance = -1; child[0] = -1;
} else if (grand_child->balance == 0) { } else if (grand_child[0] == 0) {
parent->balance = 0; parent[0] = 0;
child->balance = 0; child[0] = 0;
} else { } else {
parent->balance = 1; parent[0] = 1;
child->balance = 0; child[0] = 0;
} }
grand_child->balance = 0; grand_child[0] = 0;
return grand_child; return grand_child;
} }
/* /*
* Performs a right-left repair. * Performs a right-left repair.
*/ */
static struct node *multiset_repair_right_left(multiset me, static char *multiset_repair_right_left(multiset me, char *const parent,
struct node *const parent, char *const child,
struct node *const child, char *const grand_child)
struct node *const grand_child)
{ {
multiset_rotate_right(me, child, grand_child); multiset_rotate_right(me, child, grand_child);
multiset_rotate_left(me, parent, grand_child); multiset_rotate_left(me, parent, grand_child);
if (grand_child->balance == 1) { if (grand_child[0] == 1) {
parent->balance = -1; parent[0] = -1;
child->balance = 0; child[0] = 0;
} else if (grand_child->balance == 0) { } else if (grand_child[0] == 0) {
parent->balance = 0; parent[0] = 0;
child->balance = 0; child[0] = 0;
} else { } else {
parent->balance = 0; parent[0] = 0;
child->balance = 1; child[0] = 1;
} }
grand_child->balance = 0; grand_child[0] = 0;
return grand_child; return grand_child;
} }
@@ -236,18 +238,16 @@ static struct node *multiset_repair_right_left(multiset me,
* Repairs the AVL tree on insert. The only possible values of parent->balance * Repairs the AVL tree on insert. The only possible values of parent->balance
* are {-2, 2} and the only possible values of child->balance are {-1, 0, 1}. * are {-2, 2} and the only possible values of child->balance are {-1, 0, 1}.
*/ */
static struct node *multiset_repair(multiset me, static char *multiset_repair(multiset me, char *const parent,
struct node *const parent, char *const child, char *const grand_child)
struct node *const child,
struct node *const grand_child)
{ {
if (parent->balance == 2) { if (parent[0] == 2) {
if (child->balance == -1) { if (child[0] == -1) {
return multiset_repair_right_left(me, parent, child, grand_child); return multiset_repair_right_left(me, parent, child, grand_child);
} }
return multiset_repair_left(me, parent, child); return multiset_repair_left(me, parent, child);
} }
if (child->balance == 1) { if (child[0] == 1) {
return multiset_repair_left_right(me, parent, child, grand_child); return multiset_repair_left_right(me, parent, child, grand_child);
} }
return multiset_repair_right(me, parent, child); return multiset_repair_right(me, parent, child);
@@ -256,55 +256,53 @@ static struct node *multiset_repair(multiset me,
/* /*
* Balances the AVL tree on insert. * Balances the AVL tree on insert.
*/ */
static void multiset_insert_balance(multiset me, struct node *const item) static void multiset_insert_balance(multiset me, char *const item)
{ {
struct node *grand_child = NULL; char *grand_child = NULL;
struct node *child = item; char *child = item;
struct node *parent = item->parent; char *parent;
memcpy(&parent, item + node_parent_offset, ptr_size);
while (parent) { while (parent) {
if (parent->left == child) { char *parent_left;
parent->balance--; memcpy(&parent_left, parent + node_left_child_offset, ptr_size);
if (parent_left == child) {
parent[0]--;
} else { } else {
parent->balance++; parent[0]++;
} }
/* If balance is zero after modification, then the tree is balanced. */ /* If balance is zero after modification, then the tree is balanced. */
if (parent->balance == 0) { if (parent[0] == 0) {
return; return;
} }
/* Must re-balance if not in {-1, 0, 1} */ /* Must re-balance if not in {-1, 0, 1} */
if (parent->balance > 1 || parent->balance < -1) { if (parent[0] > 1 || parent[0] < -1) {
/* After one repair, the tree is balanced. */ /* After one repair, the tree is balanced. */
multiset_repair(me, parent, child, grand_child); multiset_repair(me, parent, child, grand_child);
return; return;
} }
grand_child = child; grand_child = child;
child = parent; child = parent;
parent = parent->parent; memcpy(&parent, parent + node_parent_offset, ptr_size);
} }
} }
/* /*
* Creates and allocates a node. * Creates and allocates a node.
*/ */
static struct node *multiset_create_node(multiset me, static char *multiset_create_node(multiset me, const void *const data,
const void *const data, char *const parent)
struct node *const parent)
{ {
struct node *const insert = malloc(sizeof(struct node)); char *insert = malloc(1 + count_size + 3 * ptr_size + me->key_size);
const size_t one = 1;
if (!insert) { if (!insert) {
return NULL; return NULL;
} }
insert->count = 1; insert[0] = 0;
insert->parent = parent; memcpy(insert + node_count_offset, &one, count_size);
insert->balance = 0; memcpy(insert + node_parent_offset, &parent, ptr_size);
insert->key = malloc(me->key_size); memset(insert + node_left_child_offset, 0, ptr_size);
if (!insert->key) { memset(insert + node_right_child_offset, 0, ptr_size);
free(insert); memcpy(insert + node_key_offset, data, me->key_size);
return NULL;
}
memcpy(insert->key, data, me->key_size);
insert->left = NULL;
insert->right = NULL;
me->size++; me->size++;
return insert; return insert;
} }
@@ -324,9 +322,9 @@ static struct node *multiset_create_node(multiset me,
*/ */
int multiset_put(multiset me, void *const key) int multiset_put(multiset me, void *const key)
{ {
struct node *traverse; char *traverse;
if (!me->root) { if (!me->root) {
struct node *insert = multiset_create_node(me, key, NULL); char *insert = multiset_create_node(me, key, NULL);
if (!insert) { if (!insert) {
return -ENOMEM; return -ENOMEM;
} }
@@ -335,33 +333,41 @@ int multiset_put(multiset me, void *const key)
} }
traverse = me->root; traverse = me->root;
for (;;) { for (;;) {
const int compare = me->comparator(key, traverse->key); const int compare = me->comparator(key, traverse + node_key_offset);
if (compare < 0) { if (compare < 0) {
if (traverse->left) { char *traverse_left;
traverse = traverse->left; memcpy(&traverse_left, traverse + node_left_child_offset, ptr_size);
if (traverse_left) {
traverse = traverse_left;
} else { } else {
struct node *insert = multiset_create_node(me, key, traverse); char *insert = multiset_create_node(me, key, traverse);
if (!insert) { if (!insert) {
return -ENOMEM; return -ENOMEM;
} }
traverse->left = insert; memcpy(traverse + node_left_child_offset, &insert, ptr_size);
multiset_insert_balance(me, insert); multiset_insert_balance(me, insert);
return 0; return 0;
} }
} else if (compare > 0) { } else if (compare > 0) {
if (traverse->right) { char *traverse_right;
traverse = traverse->right; memcpy(&traverse_right, traverse + node_right_child_offset,
ptr_size);
if (traverse_right) {
traverse = traverse_right;
} else { } else {
struct node *insert = multiset_create_node(me, key, traverse); char *insert = multiset_create_node(me, key, traverse);
if (!insert) { if (!insert) {
return -ENOMEM; return -ENOMEM;
} }
traverse->right = insert; memcpy(traverse + node_right_child_offset, &insert, ptr_size);
multiset_insert_balance(me, insert); multiset_insert_balance(me, insert);
return 0; return 0;
} }
} else { } else {
traverse->count++; size_t count;
memcpy(&count, traverse + node_count_offset, count_size);
count++;
memcpy(traverse + node_count_offset, &count, count_size);
me->size++; me->size++;
return 0; return 0;
} }
@@ -371,23 +377,28 @@ int multiset_put(multiset me, void *const key)
/* /*
* If a match occurs, returns the match. Else, returns NULL. * If a match occurs, returns the match. Else, returns NULL.
*/ */
static struct node *multiset_equal_match(multiset me, const void *const key) static char *multiset_equal_match(multiset me, const void *const key)
{ {
struct node *traverse = me->root; char *traverse = me->root;
if (!traverse) { if (!traverse) {
return 0; return NULL;
} }
for (;;) { for (;;) {
const int compare = me->comparator(key, traverse->key); const int compare = me->comparator(key, traverse + node_key_offset);
if (compare < 0) { if (compare < 0) {
if (traverse->left) { char *traverse_left;
traverse = traverse->left; memcpy(&traverse_left, traverse + node_left_child_offset, ptr_size);
if (traverse_left) {
traverse = traverse_left;
} else { } else {
return NULL; return NULL;
} }
} else if (compare > 0) { } else if (compare > 0) {
if (traverse->right) { char *traverse_right;
traverse = traverse->right; memcpy(&traverse_right, traverse + node_right_child_offset,
ptr_size);
if (traverse_right) {
traverse = traverse_right;
} else { } else {
return NULL; return NULL;
} }
@@ -409,13 +420,15 @@ static struct node *multiset_equal_match(multiset me, const void *const key)
* *
* @return the count of a specific key in the multi-set * @return the count of a specific key in the multi-set
*/ */
int multiset_count(multiset me, void *const key) size_t multiset_count(multiset me, void *const key)
{ {
const struct node *const item = multiset_equal_match(me, key); const char *const item = multiset_equal_match(me, key);
size_t item_count;
if (!item) { if (!item) {
return 0; return 0;
} }
return item->count; memcpy(&item_count, item + node_count_offset, count_size);
return item_count;
} }
/** /**
@@ -438,45 +451,56 @@ int multiset_contains(multiset me, void *const key)
/* /*
* Repairs the AVL tree by pivoting on an item. * Repairs the AVL tree by pivoting on an item.
*/ */
static struct node *multiset_repair_pivot(multiset me, static char *multiset_repair_pivot(multiset me, char *const item,
struct node *const item, const int is_left_pivot)
const int is_left_pivot)
{ {
struct node *const child = is_left_pivot ? item->right : item->left; char *child;
struct node *const grand_child = char *grand_child;
child->balance == 1 ? child->right : child->left; char *item_right;
char *item_left;
char *child_right;
char *child_left;
memcpy(&item_right, item + node_right_child_offset, ptr_size);
memcpy(&item_left, item + node_left_child_offset, ptr_size);
child = is_left_pivot ? item_right : item_left;
memcpy(&child_right, child + node_right_child_offset, ptr_size);
memcpy(&child_left, child + node_left_child_offset, ptr_size);
grand_child = child[0] == 1 ? child_right : child_left;
return multiset_repair(me, item, child, grand_child); return multiset_repair(me, item, child, grand_child);
} }
/* /*
* Goes back up the tree repairing it along the way. * Goes back up the tree repairing it along the way.
*/ */
static void multiset_trace_ancestors(multiset me, struct node *item) static void multiset_trace_ancestors(multiset me, char *item)
{ {
struct node *child = item; char *child = item;
struct node *parent = item->parent; char *parent;
memcpy(&parent, item + node_parent_offset, ptr_size);
while (parent) { while (parent) {
if (parent->left == child) { char *parent_left;
parent->balance++; memcpy(&parent_left, parent + node_left_child_offset, ptr_size);
if (parent_left == child) {
parent[0]++;
} else { } else {
parent->balance--; parent[0]--;
} }
/* The tree is balanced if balance is -1 or +1 after modification. */ /* The tree is balanced if balance is -1 or +1 after modification. */
if (parent->balance == -1 || parent->balance == 1) { if (parent[0] == -1 || parent[0] == 1) {
return; return;
} }
/* Must re-balance if not in {-1, 0, 1} */ /* Must re-balance if not in {-1, 0, 1} */
if (parent->balance > 1 || parent->balance < -1) { if (parent[0] > 1 || parent[0] < -1) {
child = multiset_repair_pivot(me, parent, parent->left == child); child = multiset_repair_pivot(me, parent, parent_left == child);
parent = child->parent; memcpy(&parent, child + node_parent_offset, ptr_size);
/* If balance is -1 or +1 after modification or the parent is */ /* If balance is -1 or +1 after modification or */
/* NULL, then the tree is balanced. */ /* the parent is NULL, then the tree is balanced. */
if (!parent || child->balance == -1 || child->balance == 1) { if (!parent || child[0] == -1 || child[0] == 1) {
return; return;
} }
} else { } else {
child = parent; child = parent;
parent = parent->parent; memcpy(&parent, parent + node_parent_offset, ptr_size);
} }
} }
} }
@@ -484,23 +508,24 @@ static void multiset_trace_ancestors(multiset me, struct node *item)
/* /*
* Balances the AVL tree on deletion. * Balances the AVL tree on deletion.
*/ */
static void multiset_delete_balance(multiset me, static void multiset_delete_balance(multiset me, char *item,
struct node *item,
const int is_left_deleted) const int is_left_deleted)
{ {
if (is_left_deleted) { if (is_left_deleted) {
item->balance++; item[0]++;
} else { } else {
item->balance--; item[0]--;
} }
/* If balance is -1 or +1 after modification, then the tree is balanced. */ /* If balance is -1 or +1 after modification, then the tree is balanced. */
if (item->balance == -1 || item->balance == 1) { if (item[0] == -1 || item[0] == 1) {
return; return;
} }
/* Must re-balance if not in {-1, 0, 1} */ /* Must re-balance if not in {-1, 0, 1} */
if (item->balance > 1 || item->balance < -1) { if (item[0] > 1 || item[0] < -1) {
char *item_parent;
item = multiset_repair_pivot(me, item, is_left_deleted); item = multiset_repair_pivot(me, item, is_left_deleted);
if (!item->parent || item->balance == -1 || item->balance == 1) { memcpy(&item_parent, item + node_parent_offset, ptr_size);
if (!item_parent || item[0] == -1 || item[0] == 1) {
return; return;
} }
} }
@@ -510,62 +535,80 @@ static void multiset_delete_balance(multiset me,
/* /*
* Removes traverse when it has no children. * Removes traverse when it has no children.
*/ */
static void multiset_remove_no_children(multiset me, static void multiset_remove_no_children(multiset me, const char *const traverse)
const struct node *const traverse)
{ {
struct node *const parent = traverse->parent; char *traverse_parent;
char *traverse_parent_left;
memcpy(&traverse_parent, traverse + node_parent_offset, ptr_size);
/* If no parent and no children, then the only node is traverse. */ /* If no parent and no children, then the only node is traverse. */
if (!parent) { if (!traverse_parent) {
me->root = NULL; me->root = NULL;
return; return;
} }
memcpy(&traverse_parent_left, traverse_parent + node_left_child_offset,
ptr_size);
/* No re-reference needed since traverse has no children. */ /* No re-reference needed since traverse has no children. */
if (parent->left == traverse) { if (traverse_parent_left == traverse) {
parent->left = NULL; memset(traverse_parent + node_left_child_offset, 0, ptr_size);
multiset_delete_balance(me, parent, 1); multiset_delete_balance(me, traverse_parent, 1);
} else { } else {
parent->right = NULL; memset(traverse_parent + node_right_child_offset, 0, ptr_size);
multiset_delete_balance(me, parent, 0); multiset_delete_balance(me, traverse_parent, 0);
} }
} }
/* /*
* Removes traverse when it has one child. * Removes traverse when it has one child.
*/ */
static void multiset_remove_one_child(multiset me, static void multiset_remove_one_child(multiset me, const char *const traverse)
const struct node *const traverse)
{ {
struct node *const parent = traverse->parent; char *traverse_parent;
char *traverse_left;
char *traverse_right;
char *traverse_parent_left;
memcpy(&traverse_parent, traverse + node_parent_offset, ptr_size);
memcpy(&traverse_left, traverse + node_left_child_offset, ptr_size);
memcpy(&traverse_right, traverse + node_right_child_offset, ptr_size);
/* If no parent, make the child of traverse the new root. */ /* If no parent, make the child of traverse the new root. */
if (!parent) { if (!traverse_parent) {
if (traverse->left) { if (traverse_left) {
traverse->left->parent = NULL; memset(traverse_left + node_parent_offset, 0, ptr_size);
me->root = traverse->left; me->root = traverse_left;
} else { } else {
traverse->right->parent = NULL; memset(traverse_right + node_parent_offset, 0, ptr_size);
me->root = traverse->right; me->root = traverse_right;
} }
return; return;
} }
memcpy(&traverse_parent_left, traverse_parent + node_left_child_offset,
ptr_size);
/* The parent of traverse now references the child of traverse. */ /* The parent of traverse now references the child of traverse. */
if (parent->left == traverse) { if (traverse_parent_left == traverse) {
if (traverse->left) { if (traverse_left) {
parent->left = traverse->left; memcpy(traverse_parent + node_left_child_offset, &traverse_left,
traverse->left->parent = parent; ptr_size);
memcpy(traverse_left + node_parent_offset, &traverse_parent,
ptr_size);
} else { } else {
parent->left = traverse->right; memcpy(traverse_parent + node_left_child_offset, &traverse_right,
traverse->right->parent = parent; ptr_size);
memcpy(traverse_right + node_parent_offset, &traverse_parent,
ptr_size);
} }
multiset_delete_balance(me, parent, 1); multiset_delete_balance(me, traverse_parent, 1);
} else { } else {
if (traverse->left) { if (traverse_left) {
parent->right = traverse->left; memcpy(traverse_parent + node_right_child_offset, &traverse_left,
traverse->left->parent = parent; ptr_size);
memcpy(traverse_left + node_parent_offset, &traverse_parent,
ptr_size);
} else { } else {
parent->right = traverse->right; memcpy(traverse_parent + node_right_child_offset, &traverse_right,
traverse->right->parent = parent; ptr_size);
memcpy(traverse_right + node_parent_offset, &traverse_parent,
ptr_size);
} }
multiset_delete_balance(me, parent, 0); multiset_delete_balance(me, traverse_parent, 0);
} }
} }
@@ -573,41 +616,73 @@ static void multiset_remove_one_child(multiset me,
* Removes traverse when it has two children. * Removes traverse when it has two children.
*/ */
static void multiset_remove_two_children(multiset me, static void multiset_remove_two_children(multiset me,
const struct node *const traverse) const char *const traverse)
{ {
struct node *item; char *item;
struct node *parent; char *item_parent;
const int is_left_deleted = traverse->right->left != NULL; char *parent;
char *traverse_parent;
char *traverse_right;
char *traverse_right_left;
int is_left_deleted;
memcpy(&traverse_right, traverse + node_right_child_offset, ptr_size);
memcpy(&traverse_right_left, traverse_right + node_left_child_offset,
ptr_size);
is_left_deleted = traverse_right_left != NULL;
if (!is_left_deleted) { if (!is_left_deleted) {
item = traverse->right; char *item_left;
memcpy(&item, traverse + node_right_child_offset, ptr_size);
parent = item; parent = item;
item->balance = traverse->balance; item[0] = traverse[0];
item->parent = traverse->parent; memcpy(item + node_parent_offset, traverse + node_parent_offset,
item->left = traverse->left; ptr_size);
item->left->parent = item; memcpy(item + node_left_child_offset, traverse + node_left_child_offset,
ptr_size);
memcpy(&item_left, item + node_left_child_offset, ptr_size);
memcpy(item_left + node_parent_offset, &item, ptr_size);
} else { } else {
item = traverse->right->left; char *item_left;
while (item->left) { char *item_right;
item = item->left; item = traverse_right_left;
memcpy(&item_left, item + node_left_child_offset, ptr_size);
while (item_left) {
item = item_left;
memcpy(&item_left, item + node_left_child_offset, ptr_size);
} }
parent = item->parent; memcpy(&parent, item + node_parent_offset, ptr_size);
item->balance = traverse->balance; item[0] = traverse[0];
item->parent->left = item->right; memcpy(&item_parent, item + node_parent_offset, ptr_size);
if (item->right) { memcpy(item_parent + node_left_child_offset,
item->right->parent = item->parent; item + node_right_child_offset, ptr_size);
memcpy(&item_right, item + node_right_child_offset, ptr_size);
if (item_right) {
memcpy(item_right + node_parent_offset, item + node_parent_offset,
ptr_size);
} }
item->left = traverse->left; memcpy(item + node_left_child_offset, traverse + node_left_child_offset,
item->left->parent = item; ptr_size);
item->right = traverse->right; memcpy(&item_left, item + node_left_child_offset, ptr_size);
item->right->parent = item; memcpy(item_left + node_parent_offset, &item, ptr_size);
item->parent = traverse->parent; memcpy(item + node_right_child_offset,
traverse + node_right_child_offset, ptr_size);
memcpy(&item_right, item + node_right_child_offset, ptr_size);
memcpy(item_right + node_parent_offset, &item, ptr_size);
memcpy(item + node_parent_offset, traverse + node_parent_offset,
ptr_size);
} }
if (!traverse->parent) { memcpy(&traverse_parent, traverse + node_parent_offset, ptr_size);
if (!traverse_parent) {
me->root = item; me->root = item;
} else if (traverse->parent->left == traverse) {
item->parent->left = item;
} else { } else {
item->parent->right = item; char *traverse_parent_left;
memcpy(&traverse_parent_left, traverse_parent + node_left_child_offset,
ptr_size);
memcpy(&item_parent, item + node_parent_offset, ptr_size);
if (traverse_parent_left == traverse) {
memcpy(item_parent + node_left_child_offset, &item, ptr_size);
} else {
memcpy(item_parent + node_right_child_offset, &item, ptr_size);
}
} }
multiset_delete_balance(me, parent, is_left_deleted); multiset_delete_balance(me, parent, is_left_deleted);
} }
@@ -615,16 +690,19 @@ static void multiset_remove_two_children(multiset me,
/* /*
* Removes the element from the set. * Removes the element from the set.
*/ */
static void multiset_remove_element(multiset me, struct node *const traverse) static void multiset_remove_element(multiset me, char *const traverse)
{ {
if (!traverse->left && !traverse->right) { char *traverse_left;
char *traverse_right;
memcpy(&traverse_left, traverse + node_left_child_offset, ptr_size);
memcpy(&traverse_right, traverse + node_right_child_offset, ptr_size);
if (!traverse_left && !traverse_right) {
multiset_remove_no_children(me, traverse); multiset_remove_no_children(me, traverse);
} else if (!traverse->left || !traverse->right) { } else if (!traverse_left || !traverse_right) {
multiset_remove_one_child(me, traverse); multiset_remove_one_child(me, traverse);
} else { } else {
multiset_remove_two_children(me, traverse); multiset_remove_two_children(me, traverse);
} }
free(traverse->key);
free(traverse); free(traverse);
} }
@@ -642,13 +720,17 @@ static void multiset_remove_element(multiset me, struct node *const traverse)
*/ */
int multiset_remove(multiset me, void *const key) int multiset_remove(multiset me, void *const key)
{ {
struct node *const traverse = multiset_equal_match(me, key); char *const traverse = multiset_equal_match(me, key);
size_t traverse_count;
if (!traverse) { if (!traverse) {
return 0; return 0;
} }
traverse->count--; memcpy(&traverse_count, traverse + node_count_offset, count_size);
if (traverse->count == 0) { if (traverse_count == 1) {
multiset_remove_element(me, traverse); multiset_remove_element(me, traverse);
} else {
traverse_count--;
memcpy(traverse + node_count_offset, &traverse_count, count_size);
} }
me->size--; me->size--;
return 1; return 1;
@@ -668,11 +750,13 @@ int multiset_remove(multiset me, void *const key)
*/ */
int multiset_remove_all(multiset me, void *const key) int multiset_remove_all(multiset me, void *const key)
{ {
struct node *const traverse = multiset_equal_match(me, key); char *const traverse = multiset_equal_match(me, key);
size_t traverse_count;
if (!traverse) { if (!traverse) {
return 0; return 0;
} }
me->size -= traverse->count; memcpy(&traverse_count, traverse + node_count_offset, count_size);
me->size -= traverse_count;
multiset_remove_element(me, traverse); multiset_remove_element(me, traverse);
return 1; return 1;
} }

View File

@@ -1,68 +1,82 @@
#include <memory.h>
#include "test.h" #include "test.h"
#include "../src/include/multiset.h" #include "../src/include/multiset.h"
/* /*
* Include this struct to verify the tree. * Include this to verify the tree.
*/ */
struct internal_multiset { struct internal_multiset {
size_t size;
size_t key_size; size_t key_size;
int (*comparator)(const void *const one, const void *const two); int (*comparator)(const void *const one, const void *const two);
int size; char *root;
struct node *root;
}; };
/* /*
* Include this struct to verify the tree. * Include this to verify the tree.
*/ */
struct node { static const size_t ptr_size = sizeof(char *);
int count; /* static const size_t count_size = sizeof(size_t); */
struct node *parent; /* Node balance is always the first byte (at index 0). */
int balance; /* static const size_t node_count_offset = sizeof(signed char); */
void *key; static const size_t node_parent_offset = 1 + sizeof(size_t);
struct node *left; static const size_t node_left_child_offset =
struct node *right; 1 + sizeof(size_t) + sizeof(char *);
}; static const size_t node_right_child_offset =
1 + sizeof(size_t) + 2 * sizeof(char *);
static const size_t node_key_offset = 1 + sizeof(size_t) + 3 * sizeof(char *);
/* /*
* Verifies that the AVL tree rules are followed. The balance factor of an item * Verifies that the AVL tree rules are followed. The balance factor of an item
* must be the right height minus the left height. Also, the left key must be * must be the right height minus the left height. Also, the left key must be
* less than the right key. * less than the right key.
*/ */
static int multiset_verify_recursive(struct node *const item) static int multiset_verify_recursive(char *const item)
{ {
int left; int left;
int right; int right;
int max; int max;
char *item_left;
char *item_right;
if (!item) { if (!item) {
return 0; return 0;
} }
left = multiset_verify_recursive(item->left); memcpy(&item_left, item + node_left_child_offset, ptr_size);
right = multiset_verify_recursive(item->right); memcpy(&item_right, item + node_right_child_offset, ptr_size);
left = multiset_verify_recursive(item_left);
right = multiset_verify_recursive(item_right);
max = left > right ? left : right; max = left > right ? left : right;
assert(right - left == item->balance); assert(right - left == item[0]);
if (item->left && item->right) { if (item_left && item_right) {
const int left_val = *(int *) item->left->key; const int left_val = *(int *) (item_left + node_key_offset);
const int right_val = *(int *) item->right->key; const int right_val = *(int *) (item_right + node_key_offset);
assert(left_val < right_val); assert(left_val < right_val);
} }
if (item->left) { if (item_left) {
assert(item->left->parent == item); char *item_left_parent;
assert(item->left->parent->key == item->key); memcpy(&item_left_parent, item_left + node_parent_offset, ptr_size);
assert(item_left_parent == item);
assert(item_left_parent + node_key_offset == item + node_key_offset);
} }
if (item->right) { if (item_right) {
assert(item->right->parent == item); char *item_right_parent;
assert(item->right->parent->key == item->key); memcpy(&item_right_parent, item_right + node_parent_offset, ptr_size);
assert(item_right_parent == item);
assert(item_right_parent + node_key_offset == item + node_key_offset);
} }
return max + 1; return max + 1;
} }
static int multiset_compute_size(struct node *const item) static size_t multiset_compute_size(char *const item)
{ {
char *left;
char *right;
if (!item) { if (!item) {
return 0; return 0;
} }
return 1 + multiset_compute_size(item->left) + memcpy(&left, item + node_left_child_offset, ptr_size);
multiset_compute_size(item->right); memcpy(&right, item + node_right_child_offset, ptr_size);
return 1 + multiset_compute_size(left) + multiset_compute_size(right);
} }
static void multiset_verify(multiset me) static void multiset_verify(multiset me)
@@ -87,7 +101,7 @@ static void test_invalid_init(void)
static void mutation_order(multiset me, const int *const arr, const int size) static void mutation_order(multiset me, const int *const arr, const int size)
{ {
int i; int i;
int actual_size = 0; size_t actual_size = 0;
assert(multiset_is_empty(me)); assert(multiset_is_empty(me));
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
int num = arr[i]; int num = arr[i];
@@ -372,7 +386,7 @@ static void test_contains(void)
static void test_stress_add(void) static void test_stress_add(void)
{ {
int count = 0; size_t count = 0;
int flip = 0; int flip = 0;
int i; int i;
multiset me = multiset_init(sizeof(int), compare_int); multiset me = multiset_init(sizeof(int), compare_int);
@@ -475,6 +489,13 @@ static void test_multiple_operations(void)
assert(multiset_count(me, &key) == 1); assert(multiset_count(me, &key) == 1);
key = 5; key = 5;
assert(multiset_count(me, &key) == 2); assert(multiset_count(me, &key) == 2);
assert(multiset_size(me) == 3);
assert(multiset_remove(me, &key));
assert(multiset_count(me, &key) == 1);
assert(multiset_size(me) == 2);
assert(multiset_remove(me, &key));
assert(multiset_count(me, &key) == 0);
assert(multiset_size(me) == 1);
multiset_remove_all(me, &key); multiset_remove_all(me, &key);
assert(multiset_size(me) == 1); assert(multiset_size(me) == 1);
key = 7; key = 7;
@@ -497,9 +518,6 @@ static void test_put_root_out_of_memory(multiset me)
int key = 2; int key = 2;
fail_malloc = 1; fail_malloc = 1;
assert(multiset_put(me, &key) == -ENOMEM); assert(multiset_put(me, &key) == -ENOMEM);
fail_malloc = 1;
delay_fail_malloc = 1;
assert(multiset_put(me, &key) == -ENOMEM);
} }
#endif #endif
@@ -509,9 +527,6 @@ static void test_put_on_left_out_of_memory(multiset me)
int key = 1; int key = 1;
fail_malloc = 1; fail_malloc = 1;
assert(multiset_put(me, &key) == -ENOMEM); assert(multiset_put(me, &key) == -ENOMEM);
fail_malloc = 1;
delay_fail_malloc = 1;
assert(multiset_put(me, &key) == -ENOMEM);
} }
#endif #endif
@@ -521,9 +536,6 @@ static void test_put_on_right_out_of_memory(multiset me)
int key = 3; int key = 3;
fail_malloc = 1; fail_malloc = 1;
assert(multiset_put(me, &key) == -ENOMEM); assert(multiset_put(me, &key) == -ENOMEM);
fail_malloc = 1;
delay_fail_malloc = 1;
assert(multiset_put(me, &key) == -ENOMEM);
} }
#endif #endif

View File

@@ -3,7 +3,7 @@
#include "../src/include/set.h" #include "../src/include/set.h"
/* /*
* Include this struct to verify the tree. * Include this to verify the tree.
*/ */
struct internal_set { struct internal_set {
size_t size; size_t size;
@@ -13,7 +13,7 @@ struct internal_set {
}; };
/* /*
* Include this struct to verify the tree. * Include this to verify the tree.
*/ */
static const size_t ptr_size = sizeof(char *); static const size_t ptr_size = sizeof(char *);
/* Node balance is always the first byte (at index 0). */ /* Node balance is always the first byte (at index 0). */