mirror of
https://github.com/bkthomps/Containers.git
synced 2025-11-16 12:34:47 +00:00
Add map
This commit is contained in:
617
src/map.c
Normal file
617
src/map.c
Normal file
@@ -0,0 +1,617 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Bailey Thompson
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <errno.h>
|
||||
#include "map.h"
|
||||
|
||||
struct _map {
|
||||
size_t key_size;
|
||||
size_t value_size;
|
||||
int (*comparator)(const void *const one, const void *const two);
|
||||
int size;
|
||||
struct node *root;
|
||||
};
|
||||
|
||||
struct node {
|
||||
struct node *parent;
|
||||
int balance;
|
||||
void *key;
|
||||
void *value;
|
||||
struct node *left;
|
||||
struct node *right;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes a map, which is a collection of key-value pairs, sorted by keys,
|
||||
* keys are unique
|
||||
*
|
||||
* @param key_size The size of each key in the map.
|
||||
* @param value_size The size of each value in the map.
|
||||
* @param comparator The comparator function used for key ordering.
|
||||
*
|
||||
* @return The newly-initialized map, or NULL if memory allocation error.
|
||||
*/
|
||||
map map_init(const size_t key_size,
|
||||
const size_t value_size,
|
||||
int (*const comparator)(const void *const, const void *const))
|
||||
{
|
||||
struct _map *const init = malloc(sizeof(struct _map));
|
||||
if (init == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
init->key_size = key_size;
|
||||
init->value_size = value_size;
|
||||
init->comparator = comparator;
|
||||
init->size = 0;
|
||||
init->root = NULL;
|
||||
return init;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the map.
|
||||
*
|
||||
* @param me The map to check.
|
||||
*
|
||||
* @return The size of the map.
|
||||
*/
|
||||
int map_size(map me)
|
||||
{
|
||||
return me->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not the map is empty.
|
||||
*
|
||||
* @param me The map to check.
|
||||
*
|
||||
* @return If the map is empty.
|
||||
*/
|
||||
bool map_is_empty(map me)
|
||||
{
|
||||
return map_size(me) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resets the parent reference.
|
||||
*/
|
||||
static void map_reference_parent(map me,
|
||||
struct node *const parent,
|
||||
struct node *const child)
|
||||
{
|
||||
child->parent = parent->parent;
|
||||
if (parent->parent == NULL) {
|
||||
me->root = child;
|
||||
} else if (parent->parent->left == parent) {
|
||||
parent->parent->left = child;
|
||||
} else {
|
||||
parent->parent->right = child;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Rotates the AVL tree to the left.
|
||||
*/
|
||||
static void map_rotate_left(map me,
|
||||
struct node *const parent,
|
||||
struct node *const child)
|
||||
{
|
||||
map_reference_parent(me, parent, child);
|
||||
struct node *const grand_child = child->left;
|
||||
if (grand_child != NULL) {
|
||||
grand_child->parent = parent;
|
||||
}
|
||||
parent->parent = child;
|
||||
parent->right = grand_child;
|
||||
child->left = parent;
|
||||
}
|
||||
|
||||
/*
|
||||
* Rotates the AVL tree to the right.
|
||||
*/
|
||||
static void map_rotate_right(map me,
|
||||
struct node *const parent,
|
||||
struct node *const child)
|
||||
{
|
||||
map_reference_parent(me, parent, child);
|
||||
struct node *const grand_child = child->right;
|
||||
if (grand_child != NULL) {
|
||||
grand_child->parent = parent;
|
||||
}
|
||||
parent->parent = child;
|
||||
parent->left = grand_child;
|
||||
child->right = parent;
|
||||
}
|
||||
|
||||
/*
|
||||
* Repairs the AVL tree on insert.
|
||||
*/
|
||||
static struct node *map_repair(map me,
|
||||
struct node *const parent,
|
||||
struct node *const child,
|
||||
struct node *const grand_child)
|
||||
{
|
||||
if (parent->balance == 2 && child->balance >= 0) {
|
||||
map_rotate_left(me, parent, child);
|
||||
if (child->balance == 0) {
|
||||
parent->balance = 1;
|
||||
child->balance = -1;
|
||||
} else {
|
||||
parent->balance = 0;
|
||||
child->balance = 0;
|
||||
}
|
||||
return child;
|
||||
}
|
||||
if (parent->balance == -2 && child->balance <= 0) {
|
||||
map_rotate_right(me, parent, child);
|
||||
if (child->balance == 0) {
|
||||
parent->balance = -1;
|
||||
child->balance = 1;
|
||||
} else {
|
||||
parent->balance = 0;
|
||||
child->balance = 0;
|
||||
}
|
||||
return child;
|
||||
}
|
||||
if (parent->balance == -2 && child->balance == 1) {
|
||||
map_rotate_left(me, child, grand_child);
|
||||
map_rotate_right(me, parent, grand_child);
|
||||
if (grand_child->balance == 1) {
|
||||
parent->balance = 0;
|
||||
child->balance = -1;
|
||||
} else if (grand_child->balance == 0) {
|
||||
parent->balance = 0;
|
||||
child->balance = 0;
|
||||
} else {
|
||||
parent->balance = 1;
|
||||
child->balance = 0;
|
||||
}
|
||||
grand_child->balance = 0;
|
||||
return grand_child;
|
||||
}
|
||||
if (parent->balance == 2 && child->balance == -1) {
|
||||
map_rotate_right(me, child, grand_child);
|
||||
map_rotate_left(me, parent, grand_child);
|
||||
if (grand_child->balance == 1) {
|
||||
parent->balance = -1;
|
||||
child->balance = 0;
|
||||
} else if (grand_child->balance == 0) {
|
||||
parent->balance = 0;
|
||||
child->balance = 0;
|
||||
} else {
|
||||
parent->balance = 0;
|
||||
child->balance = 1;
|
||||
}
|
||||
grand_child->balance = 0;
|
||||
return grand_child;
|
||||
}
|
||||
// Impossible to get here.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Balances the AVL tree on insert.
|
||||
*/
|
||||
static void map_insert_balance(map me, struct node *const item)
|
||||
{
|
||||
struct node *grand_child = NULL;
|
||||
struct node *child = item;
|
||||
struct node *parent = item->parent;
|
||||
while (parent != NULL) {
|
||||
if (parent->left == child) {
|
||||
parent->balance--;
|
||||
} else {
|
||||
parent->balance++;
|
||||
}
|
||||
// If balance is zero after modification, then the tree is balanced.
|
||||
if (parent->balance == 0) {
|
||||
return;
|
||||
}
|
||||
// Must re-balance if not in {-1, 0, 1}
|
||||
if (parent->balance > 1 || parent->balance < -1) {
|
||||
// After one repair, the tree is balanced.
|
||||
map_repair(me, parent, child, grand_child);
|
||||
return;
|
||||
}
|
||||
grand_child = child;
|
||||
child = parent;
|
||||
parent = parent->parent;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates and allocates a node.
|
||||
*/
|
||||
static struct node *map_create_node(map me,
|
||||
const void *const key,
|
||||
const void *const value,
|
||||
struct node *const parent)
|
||||
{
|
||||
struct node *const insert = malloc(sizeof(struct node));
|
||||
if (insert == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
insert->parent = parent;
|
||||
insert->balance = 0;
|
||||
insert->key = malloc(me->key_size);
|
||||
if (insert->key == NULL) {
|
||||
free(insert);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(insert->key, key, me->key_size);
|
||||
insert->value = malloc(me->value_size);
|
||||
if (insert->value == NULL) {
|
||||
free(insert->key);
|
||||
free(insert);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(insert->value, value, me->value_size);
|
||||
insert->left = NULL;
|
||||
insert->right = NULL;
|
||||
me->size++;
|
||||
return insert;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a key-value pair to the map. If the map already contains the key, the
|
||||
* value is updated to the new value.
|
||||
*
|
||||
* @param me The map to add to.
|
||||
* @param key The key to add.
|
||||
* @param value The value to add.
|
||||
*
|
||||
* @return 0 No error.
|
||||
* -ENOMEM Out of memory.
|
||||
*/
|
||||
int map_put(map me, void *const key, void *const value)
|
||||
{
|
||||
if (me->root == NULL) {
|
||||
struct node *insert = map_create_node(me, key, value, NULL);
|
||||
if (insert == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
me->root = insert;
|
||||
return 0;
|
||||
}
|
||||
struct node *traverse = me->root;
|
||||
while (true) {
|
||||
const int compare = me->comparator(key, traverse->key);
|
||||
if (compare < 0) {
|
||||
if (traverse->left != NULL) {
|
||||
traverse = traverse->left;
|
||||
} else {
|
||||
struct node *insert = map_create_node(me, key, value, traverse);
|
||||
if (insert == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
traverse->left = insert;
|
||||
map_insert_balance(me, insert);
|
||||
return 0;
|
||||
}
|
||||
} else if (compare > 0) {
|
||||
if (traverse->right != NULL) {
|
||||
traverse = traverse->right;
|
||||
} else {
|
||||
struct node *insert = map_create_node(me, key, value, traverse);
|
||||
if (insert == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
traverse->right = insert;
|
||||
map_insert_balance(me, insert);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
memcpy(traverse->value, value, me->value_size);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If a match occurs, returns the match. Else, returns NULL.
|
||||
*/
|
||||
static struct node *map_equal_match(map me, const void *const key)
|
||||
{
|
||||
struct node *traverse = me->root;
|
||||
if (traverse == NULL) {
|
||||
return false;
|
||||
}
|
||||
while (true) {
|
||||
const int compare = me->comparator(key, traverse->key);
|
||||
if (compare < 0) {
|
||||
if (traverse->left != NULL) {
|
||||
traverse = traverse->left;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
} else if (compare > 0) {
|
||||
if (traverse->right != NULL) {
|
||||
traverse = traverse->right;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
return traverse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value associated with a key in the map.
|
||||
*
|
||||
* @param value The value to copy to.
|
||||
* @param me The map to get from.
|
||||
* @param key The key to search for.
|
||||
*
|
||||
* @return If the map contained the key-value pair.
|
||||
*/
|
||||
bool map_get(void *const value, map me, void *const key)
|
||||
{
|
||||
struct node *const traverse = map_equal_match(me, key);
|
||||
if (traverse == NULL) {
|
||||
return false;
|
||||
}
|
||||
memcpy(value, traverse->value, me->value_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the map contains the specified key.
|
||||
*
|
||||
* @param me The map to check for the element.
|
||||
* @param key The key to check.
|
||||
*
|
||||
* @return If the map contained the element.
|
||||
*/
|
||||
bool map_contains(map me, void *const key)
|
||||
{
|
||||
return map_equal_match(me, key) != NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Repairs the AVL tree by pivoting on an item.
|
||||
*/
|
||||
static struct node *map_repair_pivot(map me,
|
||||
struct node *const item,
|
||||
const bool is_left_pivot)
|
||||
{
|
||||
struct node *const child = is_left_pivot ? item->right : item->left;
|
||||
struct node *const grand_child =
|
||||
child->balance == 1 ? child->right : child->left;
|
||||
return map_repair(me, item, child, grand_child);
|
||||
}
|
||||
|
||||
/*
|
||||
* Balances the AVL tree on deletion.
|
||||
*/
|
||||
static void map_delete_balance(map me,
|
||||
struct node *item,
|
||||
const bool is_left_deleted)
|
||||
{
|
||||
if (is_left_deleted) {
|
||||
item->balance++;
|
||||
} else {
|
||||
item->balance--;
|
||||
}
|
||||
// If balance is -1 or +1 after modification, then the tree is balanced.
|
||||
if (item->balance == -1 || item->balance == 1) {
|
||||
return;
|
||||
}
|
||||
// Must re-balance if not in {-1, 0, 1}
|
||||
if (item->balance > 1 || item->balance < -1) {
|
||||
item = map_repair_pivot(me, item, is_left_deleted);
|
||||
if (item->parent == NULL || item->balance == -1 || item->balance == 1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
struct node *child = item;
|
||||
struct node *parent = item->parent;
|
||||
while (parent != NULL) {
|
||||
if (parent->left == child) {
|
||||
parent->balance++;
|
||||
} else {
|
||||
parent->balance--;
|
||||
}
|
||||
// If balance is -1 or +1 after modification, then the tree is balanced.
|
||||
if (parent->balance == -1 || parent->balance == 1) {
|
||||
return;
|
||||
}
|
||||
// Must re-balance if not in {-1, 0, 1}
|
||||
if (parent->balance > 1 || parent->balance < -1) {
|
||||
child = map_repair_pivot(me, parent, parent->left == child);
|
||||
parent = child->parent;
|
||||
// If balance is -1 or +1 after modification or the parent is NULL,
|
||||
// then the tree is balanced.
|
||||
if (parent == NULL || child->balance == -1 || child->balance == 1) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
child = parent;
|
||||
parent = parent->parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes traverse when it has no children.
|
||||
*/
|
||||
static void map_remove_no_children(map me, const struct node *const traverse)
|
||||
{
|
||||
struct node *const parent = traverse->parent;
|
||||
// If no parent and no children, then the only node is traverse.
|
||||
if (parent == NULL) {
|
||||
me->root = NULL;
|
||||
return;
|
||||
}
|
||||
// No re-reference needed since traverse has no children.
|
||||
if (parent->left == traverse) {
|
||||
parent->left = NULL;
|
||||
map_delete_balance(me, parent, true);
|
||||
} else {
|
||||
parent->right = NULL;
|
||||
map_delete_balance(me, parent, false);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes traverse when it has one child.
|
||||
*/
|
||||
static void map_remove_one_child(map me, const struct node *const traverse)
|
||||
{
|
||||
struct node *const parent = traverse->parent;
|
||||
// If no parent, make the child of traverse the new root.
|
||||
if (parent == NULL) {
|
||||
if (traverse->left != NULL) {
|
||||
traverse->left->parent = NULL;
|
||||
me->root = traverse->left;
|
||||
} else {
|
||||
traverse->right->parent = NULL;
|
||||
me->root = traverse->right;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// The parent of traverse now references the child of traverse.
|
||||
if (parent->left == traverse) {
|
||||
if (traverse->left != NULL) {
|
||||
parent->left = traverse->left;
|
||||
traverse->left->parent = parent;
|
||||
} else {
|
||||
parent->left = traverse->right;
|
||||
traverse->right->parent = parent;
|
||||
}
|
||||
map_delete_balance(me, parent, true);
|
||||
} else {
|
||||
if (traverse->left != NULL) {
|
||||
parent->right = traverse->left;
|
||||
traverse->left->parent = parent;
|
||||
} else {
|
||||
parent->right = traverse->right;
|
||||
traverse->right->parent = parent;
|
||||
}
|
||||
map_delete_balance(me, parent, false);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes traverse when it has two children.
|
||||
*/
|
||||
static void map_remove_two_children(map me, const struct node *const traverse)
|
||||
{
|
||||
struct node *item;
|
||||
struct node *parent;
|
||||
const bool is_left_deleted = traverse->right->left != NULL;
|
||||
if (!is_left_deleted) {
|
||||
item = traverse->right;
|
||||
parent = item;
|
||||
item->balance = traverse->balance;
|
||||
item->parent = traverse->parent;
|
||||
item->left = traverse->left;
|
||||
item->left->parent = item;
|
||||
} else {
|
||||
item = traverse->right->left;
|
||||
while (item->left != NULL) {
|
||||
item = item->left;
|
||||
}
|
||||
parent = item->parent;
|
||||
item->balance = traverse->balance;
|
||||
item->parent->left = item->right;
|
||||
if (item->right != NULL) {
|
||||
item->right->parent = item->parent;
|
||||
}
|
||||
item->left = traverse->left;
|
||||
item->left->parent = item;
|
||||
item->right = traverse->right;
|
||||
item->right->parent = item;
|
||||
item->parent = traverse->parent;
|
||||
}
|
||||
if (traverse->parent == NULL) {
|
||||
me->root = item;
|
||||
} else if (traverse->parent->left == traverse) {
|
||||
item->parent->left = item;
|
||||
} else {
|
||||
item->parent->right = item;
|
||||
}
|
||||
map_delete_balance(me, parent, is_left_deleted);
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes the element from the map.
|
||||
*/
|
||||
static void map_remove_element(map me, struct node *const traverse)
|
||||
{
|
||||
if (traverse->left == NULL && traverse->right == NULL) {
|
||||
map_remove_no_children(me, traverse);
|
||||
} else if (traverse->left == NULL || traverse->right == NULL) {
|
||||
map_remove_one_child(me, traverse);
|
||||
} else {
|
||||
map_remove_two_children(me, traverse);
|
||||
}
|
||||
free(traverse->key);
|
||||
free(traverse->value);
|
||||
free(traverse);
|
||||
me->size--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the key-value pair from the map if it contains it.
|
||||
*
|
||||
* @param me The map to remove an element from.
|
||||
* @param key The key to remove.
|
||||
*
|
||||
* @return If the map contained the key-value pair.
|
||||
*/
|
||||
bool map_remove(map me, void *const key)
|
||||
{
|
||||
struct node *const traverse = map_equal_match(me, key);
|
||||
if (traverse == NULL) {
|
||||
return false;
|
||||
}
|
||||
map_remove_element(me, traverse);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the key-value pairs from the map.
|
||||
*
|
||||
* @param me The map to clear.
|
||||
*/
|
||||
void map_clear(map me)
|
||||
{
|
||||
while (me->root != NULL) {
|
||||
map_remove_element(me, me->root);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the map memory.
|
||||
*
|
||||
* @param me The map to free from memory.
|
||||
*
|
||||
* @return NULL
|
||||
*/
|
||||
map map_destroy(map me)
|
||||
{
|
||||
map_clear(me);
|
||||
free(me);
|
||||
return NULL;
|
||||
}
|
||||
49
src/map.h
Normal file
49
src/map.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Bailey Thompson
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef CONTAINERS_MAP_H
|
||||
#define CONTAINERS_MAP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct _map *map;
|
||||
|
||||
// Starting
|
||||
map map_init(size_t key_size,
|
||||
size_t value_size,
|
||||
int (*comparator)(const void *const one, const void *const two));
|
||||
|
||||
// Capacity
|
||||
int map_size(map me);
|
||||
bool map_is_empty(map me);
|
||||
|
||||
// Accessing
|
||||
int map_put(map me, void *key, void *value);
|
||||
bool map_get(void *value, map me, void *key);
|
||||
bool map_contains(map me, void *key);
|
||||
bool map_remove(map me, void *key);
|
||||
|
||||
// Ending
|
||||
void map_clear(map me);
|
||||
map map_destroy(map me);
|
||||
|
||||
#endif /* CONTAINERS_MAP_H */
|
||||
@@ -42,13 +42,13 @@ struct node {
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes a multiset, which is a collection of key-value pairs, sorted by
|
||||
* Initializes a multi-set, which is a collection of key-value pairs, sorted by
|
||||
* keys, keys are unique
|
||||
*
|
||||
* @param key_size The size of each element in the multiset.
|
||||
* @param key_size The size of each element in the multi-set.
|
||||
* @param comparator The comparator function used for key ordering.
|
||||
*
|
||||
* @return The newly-initialized multiset, or NULL if memory allocation error.
|
||||
* @return The newly-initialized multi-set, or NULL if memory allocation error.
|
||||
*/
|
||||
multiset multiset_init(const size_t key_size,
|
||||
int (*const comparator)(const void *const,
|
||||
@@ -66,11 +66,11 @@ multiset multiset_init(const size_t key_size,
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the multiset.
|
||||
* Gets the size of the multi-set.
|
||||
*
|
||||
* @param me The multiset to check.
|
||||
* @param me The multi-set to check.
|
||||
*
|
||||
* @return The size of the multiset.
|
||||
* @return The size of the multi-set.
|
||||
*/
|
||||
int multiset_size(multiset me)
|
||||
{
|
||||
@@ -78,11 +78,11 @@ int multiset_size(multiset me)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not the multiset is empty.
|
||||
* Determines whether or not the multi-set is empty.
|
||||
*
|
||||
* @param me The multiset to check.
|
||||
* @param me The multi-set to check.
|
||||
*
|
||||
* @return If the multiset is empty.
|
||||
* @return If the multi-set is empty.
|
||||
*/
|
||||
bool multiset_is_empty(multiset me)
|
||||
{
|
||||
@@ -262,10 +262,10 @@ static struct node *multiset_create_node(multiset me,
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an element to the multiset if the multiset does not already contain it.
|
||||
* Adds a key to the multi-set.
|
||||
*
|
||||
* @param me The multiset to add to.
|
||||
* @param key The element to add.
|
||||
* @param me The multi-set to add to.
|
||||
* @param key The key to add.
|
||||
*
|
||||
* @return 0 No error.
|
||||
* -ENOMEM Out of memory.
|
||||
@@ -348,12 +348,12 @@ static struct node *multiset_equal_match(multiset me, const void *const key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the count of a specific key in the multiset.
|
||||
* Determines the count of a specific key in the multi-set.
|
||||
*
|
||||
* @param me The multiset to check for the count.
|
||||
* @param key The element to check.
|
||||
* @param me The multi-set to check for the count.
|
||||
* @param key The key to check.
|
||||
*
|
||||
* @return The count of a specific key in the multiset.
|
||||
* @return The count of a specific key in the multi-set.
|
||||
*/
|
||||
int multiset_count(multiset me, void *const key)
|
||||
{
|
||||
@@ -365,12 +365,12 @@ int multiset_count(multiset me, void *const key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the multiset contains the specified element.
|
||||
* Determines if the multi-set contains the specified key.
|
||||
*
|
||||
* @param me The multiset to check for the element.
|
||||
* @param key The element to check.
|
||||
* @param me The multi-set to check for the key.
|
||||
* @param key The key to check.
|
||||
*
|
||||
* @return If the multiset contained the element.
|
||||
* @return If the multiset contained the key.
|
||||
*/
|
||||
bool multiset_contains(multiset me, void *const key)
|
||||
{
|
||||
@@ -563,12 +563,12 @@ static void multiset_remove_element(multiset me, struct node *const traverse)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an element from the multiset if it contains it.
|
||||
* Removes a key from the multi-set if it contains it.
|
||||
*
|
||||
* @param me The multiset to remove an element from.
|
||||
* @param key The element to remove.
|
||||
* @param me The multi-set to remove a key from.
|
||||
* @param key The key to remove.
|
||||
*
|
||||
* @return If the multiset contained the element.
|
||||
* @return If the multi-set contained the key.
|
||||
*/
|
||||
bool multiset_remove(multiset me, void *const key)
|
||||
{
|
||||
@@ -585,13 +585,12 @@ bool multiset_remove(multiset me, void *const key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all the elements specified by the key from an multiset if it contains
|
||||
* the key.
|
||||
* Removes all the occurrences of a specified key in the multi-set.
|
||||
*
|
||||
* @param me The multiset to remove an element from.
|
||||
* @param key The element to remove.
|
||||
* @param me The multi-set to remove a key from.
|
||||
* @param key The key to remove.
|
||||
*
|
||||
* @return If the multiset contained the element.
|
||||
* @return If the multi-set contained the key.
|
||||
*/
|
||||
bool multiset_remove_all(multiset me, void *const key)
|
||||
{
|
||||
@@ -605,9 +604,9 @@ bool multiset_remove_all(multiset me, void *const key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the elements from the multiset.
|
||||
* Clears the keys from the multiset.
|
||||
*
|
||||
* @param me The multiset to clear.
|
||||
* @param me The multi-set to clear.
|
||||
*/
|
||||
void multiset_clear(multiset me)
|
||||
{
|
||||
@@ -618,9 +617,9 @@ void multiset_clear(multiset me)
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the multiset memory.
|
||||
* Frees the multi-set memory.
|
||||
*
|
||||
* @param me The multiset to free from memory.
|
||||
* @param me The multi-set to free from memory.
|
||||
*
|
||||
* @return NULL
|
||||
*/
|
||||
|
||||
22
src/set.c
22
src/set.c
@@ -259,10 +259,10 @@ static struct node *set_create_node(set me,
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an element to the set if the set does not already contain it.
|
||||
* Adds a key to the set if the set does not already contain it.
|
||||
*
|
||||
* @param me The set to add to.
|
||||
* @param key The element to add.
|
||||
* @param key The key to add.
|
||||
*
|
||||
* @return 0 No error.
|
||||
* -ENOMEM Out of memory.
|
||||
@@ -340,12 +340,12 @@ static struct node *set_equal_match(set me, const void *const key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the set contains the specified element.
|
||||
* Determines if the set contains the specified key.
|
||||
*
|
||||
* @param me The set to check for the element.
|
||||
* @param key The element to check.
|
||||
* @param me The set to check for the key.
|
||||
* @param key The key to check.
|
||||
*
|
||||
* @return If the set contained the element.
|
||||
* @return If the set contained the key.
|
||||
*/
|
||||
bool set_contains(set me, void *const key)
|
||||
{
|
||||
@@ -536,12 +536,12 @@ static void set_remove_element(set me, struct node *const traverse)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the element from the set if it contains it.
|
||||
* Removes the key from the set if it contains it.
|
||||
*
|
||||
* @param me The set to remove an element from.
|
||||
* @param key The element to remove.
|
||||
* @param me The set to remove an key from.
|
||||
* @param key The key to remove.
|
||||
*
|
||||
* @return If the set contained the element.
|
||||
* @return If the set contained the key.
|
||||
*/
|
||||
bool set_remove(set me, void *const key)
|
||||
{
|
||||
@@ -554,7 +554,7 @@ bool set_remove(set me, void *const key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the elements from the set.
|
||||
* Clears the keys from the set.
|
||||
*
|
||||
* @param me The set to clear.
|
||||
*/
|
||||
|
||||
@@ -221,8 +221,8 @@ static struct node *const unordered_map_create_element(unordered_map me,
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a key-value pair to the unordered map if the unordered map does not
|
||||
* already contain it.
|
||||
* Adds a key-value pair to the unordered map. If the unordered map already
|
||||
* contains the key, the value is updated to the new value.
|
||||
*
|
||||
* @param me The unordered map to add to.
|
||||
* @param key The key to add.
|
||||
|
||||
@@ -241,8 +241,7 @@ unordered_multimap_create_element(unordered_multimap me,
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a key-value pair to the unordered multi-map if the unordered multi-map
|
||||
* does not already contain it.
|
||||
* Adds a key-value pair to the unordered multi-map.
|
||||
*
|
||||
* @param me The unordered multi-map to add to.
|
||||
* @param key The key to add.
|
||||
|
||||
@@ -50,7 +50,7 @@ struct node {
|
||||
* Initializes an unordered multi-set, which is a collection of keys, hashed by
|
||||
* keys.
|
||||
*
|
||||
* @param key_size The size of each element in the unordered multi-set.
|
||||
* @param key_size The size of each key in the unordered multi-set.
|
||||
* @param hash The hash function which computes the hash from the key.
|
||||
* @param comparator The comparator function which compares two keys.
|
||||
*
|
||||
@@ -215,8 +215,7 @@ unordered_multiset_create_element(unordered_multiset me,
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an element to the unordered multi-set if the unordered multi-set does
|
||||
* not already contain it.
|
||||
* Adds an element to the unordered multi-set.
|
||||
*
|
||||
* @param me The unordered multi-set to add to.
|
||||
* @param key The element to add.
|
||||
@@ -287,10 +286,10 @@ int unordered_multiset_count(unordered_multiset me, void *const key)
|
||||
/**
|
||||
* Determines if the unordered multi-set contains the specified element.
|
||||
*
|
||||
* @param me The unordered multi-set to check for the element.
|
||||
* @param key The element to check.
|
||||
* @param me The unordered multi-set to check for the key.
|
||||
* @param key The key to check.
|
||||
*
|
||||
* @return If the unordered multi-set contained the element.
|
||||
* @return If the unordered multi-set contained the key.
|
||||
*/
|
||||
bool unordered_multiset_contains(unordered_multiset me, void *const key)
|
||||
{
|
||||
@@ -298,12 +297,12 @@ bool unordered_multiset_contains(unordered_multiset me, void *const key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an element from the unordered multi-set if it contains it.
|
||||
* Removes a key from the unordered multi-set if it contains it.
|
||||
*
|
||||
* @param me The unordered multi-set to remove an element from.
|
||||
* @param key The element to remove.
|
||||
* @param me The unordered multi-set to remove a key from.
|
||||
* @param key The key to remove.
|
||||
*
|
||||
* @return If the unordered multi-set contained the element.
|
||||
* @return If the unordered multi-set contained the key.
|
||||
*/
|
||||
bool unordered_multiset_remove(unordered_multiset me, void *const key)
|
||||
{
|
||||
@@ -343,13 +342,13 @@ bool unordered_multiset_remove(unordered_multiset me, void *const key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all the elements specified by the key from an unordered multi-set if
|
||||
* it contains the key.
|
||||
* Removes all the keys specified by the key from an unordered multi-set if it
|
||||
* contains the key.
|
||||
*
|
||||
* @param me The unordered multi-set to remove an element from.
|
||||
* @param key The element to remove.
|
||||
* @param me The unordered multi-set to remove a key from.
|
||||
* @param key The key to remove.
|
||||
*
|
||||
* @return If the unordered multi-set contained the element.
|
||||
* @return If the unordered multi-set contained the key.
|
||||
*/
|
||||
bool unordered_multiset_remove_all(unordered_multiset me, void *const key)
|
||||
{
|
||||
@@ -383,7 +382,7 @@ bool unordered_multiset_remove_all(unordered_multiset me, void *const key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the elements from the unordered multi-set.
|
||||
* Clears the keys from the unordered multi-set.
|
||||
*
|
||||
* @param me The unordered multi-set to clear.
|
||||
*
|
||||
|
||||
@@ -48,7 +48,7 @@ struct node {
|
||||
* Initializes an unordered set, which is a collection of unique keys, hashed by
|
||||
* keys.
|
||||
*
|
||||
* @param key_size The size of each element in the unordered set.
|
||||
* @param key_size The size of each key in the unordered set.
|
||||
* @param hash The hash function which computes the hash from the key.
|
||||
* @param comparator The comparator function which compares two keys.
|
||||
*
|
||||
@@ -273,12 +273,12 @@ bool unordered_set_contains(unordered_set me, void *const key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the element from the unordered set if it contains it.
|
||||
* Removes the key from the unordered set if it contains it.
|
||||
*
|
||||
* @param me The unordered set to remove an element from.
|
||||
* @param key The element to remove.
|
||||
* @param me The unordered set to remove an key from.
|
||||
* @param key The key to remove.
|
||||
*
|
||||
* @return If the unordered set contained the element.
|
||||
* @return If the unordered set contained the key.
|
||||
*/
|
||||
bool unordered_set_remove(unordered_set me, void *const key)
|
||||
{
|
||||
@@ -310,7 +310,7 @@ bool unordered_set_remove(unordered_set me, void *const key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the elements from the unordered set.
|
||||
* Clears the keys from the unordered set.
|
||||
*
|
||||
* @param me The unordered set to clear.
|
||||
*
|
||||
|
||||
119
tst/map.c
Normal file
119
tst/map.c
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "test.h"
|
||||
#include "../src/map.h"
|
||||
|
||||
static int compare_int(const void *const one, const void *const two)
|
||||
{
|
||||
const int a = *(int *) one;
|
||||
const int b = *(int *) two;
|
||||
return a - b;
|
||||
}
|
||||
|
||||
void test_map(void)
|
||||
{
|
||||
map a = map_init(sizeof(int), sizeof(int), compare_int);
|
||||
assert(map_size(a) == 0);
|
||||
assert(map_is_empty(a));
|
||||
int b = 4;
|
||||
int c = 9;
|
||||
map_put(a, &b, &c);
|
||||
assert(map_size(a) == 1);
|
||||
c = 5;
|
||||
map_put(a, &b, &c);
|
||||
assert(map_size(a) == 1);
|
||||
assert(!map_is_empty(a));
|
||||
assert(map_contains(a, &b));
|
||||
c = 0xdeadbeef;
|
||||
map_get(&c, a, &b);
|
||||
assert(c == 5);
|
||||
b = 7;
|
||||
assert(!map_contains(a, &b));
|
||||
map_put(a, &b, &c);
|
||||
assert(map_size(a) == 2);
|
||||
assert(map_contains(a, &b));
|
||||
int d[10] = {5, 9, 4, -5, 0, 6, 1, 5, 7, 2};
|
||||
for (int i = 0; i < 10; i++) {
|
||||
map_put(a, &d[i], &c);
|
||||
assert(map_contains(a, &d[i]));
|
||||
}
|
||||
assert(map_size(a) == 9);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assert(map_contains(a, &d[i]));
|
||||
}
|
||||
for (int i = -100; i < 100; i++) {
|
||||
bool contains = false;
|
||||
for (int j = 0; j < 10; j++) {
|
||||
if (d[j] == i) {
|
||||
contains = true;
|
||||
}
|
||||
}
|
||||
assert(map_contains(a, &i) == contains);
|
||||
}
|
||||
int num = -3;
|
||||
assert(!map_remove(a, &num));
|
||||
assert(map_size(a) == 9);
|
||||
assert(!map_contains(a, &num));
|
||||
num = 6;
|
||||
assert(map_remove(a, &num));
|
||||
assert(map_size(a) == 8);
|
||||
assert(!map_contains(a, &num));
|
||||
num = 4;
|
||||
assert(map_remove(a, &num));
|
||||
assert(map_size(a) == 7);
|
||||
assert(!map_contains(a, &num));
|
||||
num = 7;
|
||||
assert(map_remove(a, &num));
|
||||
assert(map_size(a) == 6);
|
||||
assert(!map_contains(a, &num));
|
||||
num = 9;
|
||||
assert(map_remove(a, &num));
|
||||
assert(map_size(a) == 5);
|
||||
assert(!map_contains(a, &num));
|
||||
num = -5;
|
||||
assert(map_remove(a, &num));
|
||||
assert(map_size(a) == 4);
|
||||
assert(!map_contains(a, &num));
|
||||
num = 0;
|
||||
assert(map_remove(a, &num));
|
||||
assert(map_size(a) == 3);
|
||||
assert(!map_contains(a, &num));
|
||||
num = 1;
|
||||
assert(map_remove(a, &num));
|
||||
assert(map_size(a) == 2);
|
||||
assert(!map_contains(a, &num));
|
||||
num = 5;
|
||||
assert(map_remove(a, &num));
|
||||
assert(map_size(a) == 1);
|
||||
assert(!map_contains(a, &num));
|
||||
num = 2;
|
||||
assert(map_remove(a, &num));
|
||||
assert(map_size(a) == 0);
|
||||
assert(!map_contains(a, &num));
|
||||
// Add a lot of items and remove individually.
|
||||
for (int i = 5000; i < 6000; i++) {
|
||||
map_put(a, &i, &c);
|
||||
assert(map_contains(a, &i));
|
||||
}
|
||||
assert(map_size(a) == 1000);
|
||||
for (int i = 5000; i < 6000; i++) {
|
||||
map_remove(a, &i);
|
||||
assert(!map_contains(a, &i));
|
||||
}
|
||||
assert(map_size(a) == 0);
|
||||
assert(map_is_empty(a));
|
||||
map_clear(a);
|
||||
assert(map_size(a) == 0);
|
||||
assert(map_is_empty(a));
|
||||
// Add a lot of items and clear.
|
||||
for (int i = 5000; i < 6000; i++) {
|
||||
map_put(a, &i, &c);
|
||||
assert(map_contains(a, &i));
|
||||
}
|
||||
assert(map_size(a) == 1000);
|
||||
map_clear(a);
|
||||
int p = 0xdeadbeef;
|
||||
assert(!map_remove(a, &p));
|
||||
assert(map_size(a) == 0);
|
||||
assert(map_is_empty(a));
|
||||
a = map_destroy(a);
|
||||
assert(a == NULL);
|
||||
}
|
||||
@@ -8,6 +8,7 @@ int main(void)
|
||||
test_forward_list();
|
||||
test_list();
|
||||
test_set();
|
||||
test_map();
|
||||
test_multiset();
|
||||
test_unordered_set();
|
||||
test_unordered_map();
|
||||
|
||||
@@ -11,6 +11,7 @@ void test_deque(void);
|
||||
void test_forward_list(void);
|
||||
void test_list(void);
|
||||
void test_set(void);
|
||||
void test_map(void);
|
||||
void test_multiset(void);
|
||||
void test_unordered_set(void);
|
||||
void test_unordered_map(void);
|
||||
|
||||
Reference in New Issue
Block a user