Implement linked list data structure.
This commit is contained in:
parent
27ea69e125
commit
4fa9ab13ee
|
@ -4,7 +4,7 @@ project(libds)
|
|||
set_property(GLOBAL PROPERTY C_STANDARD 90)
|
||||
ADD_COMPILE_OPTIONS(-pedantic -Wall)
|
||||
|
||||
add_library(ds STATIC src/libds.c src/vec.c src/ht.c)
|
||||
add_library(ds STATIC src/libds.c src/vec.c src/ht.c src/ll.c)
|
||||
add_executable(libds src/main.c)
|
||||
|
||||
target_include_directories(ds PUBLIC include)
|
||||
|
|
113
include/ll.h
Normal file
113
include/ll.h
Normal file
|
@ -0,0 +1,113 @@
|
|||
#ifndef LIBDS_LL_HEADER
|
||||
#define LIBDS_LL_HEADER
|
||||
|
||||
#include "libds.h"
|
||||
|
||||
/**
|
||||
* A simple linked list node, doubly-linked.
|
||||
*/
|
||||
struct ll_node_s {
|
||||
struct ll_node_s* next;
|
||||
struct ll_node_s* prev;
|
||||
void* data;
|
||||
};
|
||||
|
||||
/**
|
||||
* A linked list struct, containing pointers to the head and tail node.
|
||||
*/
|
||||
struct ll_s {
|
||||
struct ll_node_s* head;
|
||||
struct ll_node_s* tail;
|
||||
};
|
||||
|
||||
typedef struct ll_node_s ll_node;
|
||||
typedef struct ll_s ll;
|
||||
|
||||
/**
|
||||
* Initializes the list with default values.
|
||||
* @param ll the linked list to initialize.
|
||||
*/
|
||||
void ll_init(ll* ll);
|
||||
/**
|
||||
* Frees the linked list and all underlying data.
|
||||
* The stored data is left intact. Use ll_foreach to free
|
||||
* the data if it is no longer used.
|
||||
* @param ll
|
||||
*/
|
||||
void ll_free(ll* ll);
|
||||
|
||||
/**
|
||||
* Adds an element to the end of the linked list.
|
||||
* @param ll the linked list to add to
|
||||
* @param data the data to add
|
||||
* @return LIBDS_SUCCESS if all goes well, LIBDS_MALLOC if an allocation fails.
|
||||
*/
|
||||
libds_result ll_append(ll* ll, void* data);
|
||||
/**
|
||||
* Adds an element to the beginning of the linked list.
|
||||
* @param ll the linked list to add to
|
||||
* @param data the data to add
|
||||
* @return LIBDS_SUCCESS if all goes well, LIBDS_MALLOC if an allocation fails.
|
||||
*/
|
||||
libds_result ll_prepend(ll* ll, void* data);
|
||||
/**
|
||||
* Removes an element from the list.
|
||||
* @param ll the linked list from which to remove.
|
||||
* @param data the data to remove.
|
||||
*/
|
||||
void ll_remove(ll* ll, void* data);
|
||||
|
||||
/**
|
||||
* Runs through every element in the linked list, and compares it against the
|
||||
* given data using the given comparison function. If the comparison function returns
|
||||
* true, returns the element that was passed to it. If the comparison function returns
|
||||
* true for no element, returns NULL.
|
||||
* @param ll the linked list to iterate through.
|
||||
* @param data the data to compare elements against
|
||||
* @param compare the comparison function
|
||||
* @return the first element that is matched by the comparison function, or NULL if none are matched.
|
||||
*/
|
||||
void* ll_find(ll* ll, void* data, compare_func compare);
|
||||
/**
|
||||
* Runs through every element in the linked list, and compares it against the
|
||||
* given data using the given comparison function. If the comparison function returns
|
||||
* true, calls the foreach function, passing it the element and the variable argument list.
|
||||
* Stops if any foreach function returns a nonzero code.
|
||||
* @param ll the linked list to perform the operation on
|
||||
* @param data the data to pass the comparison function
|
||||
* @param compare the comparison function operating on the data and each element in the list.
|
||||
* @param foreach the function to be called on every element recognized by the comparison function
|
||||
* @param ... variable arguments to be passed on to the foreach function
|
||||
* @return 0 if all goes well, or the first nonzero code returned by foreach.
|
||||
*/
|
||||
int ll_foreach(ll* ll, void* data, compare_func compare, foreach_func foreach, ...);
|
||||
|
||||
/**
|
||||
* Gets the element at the beginning of the linked list.
|
||||
* @param ll the linked list to get the data from.
|
||||
* @return the element at the beginning of the element, or NULL if there is none.
|
||||
*/
|
||||
void* ll_head(ll* ll);
|
||||
/**
|
||||
* Gets the element at the end of the linked list.
|
||||
* @param ll the linked list to get the data from.
|
||||
* @return the element at the end of the element, or NULL if there is none.
|
||||
*/
|
||||
void* ll_tail(ll* ll);
|
||||
/**
|
||||
* Removes the element at the beginning of the linked list,
|
||||
* and returns it.
|
||||
* @param ll the linked list to perform the operation on.
|
||||
* @return the value removed from the beginning of the linked list, or NULL if there was none.
|
||||
*/
|
||||
void* ll_pophead(ll* ll);
|
||||
/**
|
||||
* Removes the element at the end of the linked list,
|
||||
* and returns it.
|
||||
* @param ll the linked list to perform the operation on.
|
||||
* @return the value removed from the end of the linked list, or NULL if there was none.
|
||||
*/
|
||||
void* ll_poptail(ll* ll);
|
||||
|
||||
|
||||
#endif
|
114
src/ll.c
Normal file
114
src/ll.c
Normal file
|
@ -0,0 +1,114 @@
|
|||
#include "ll.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
void ll_init(ll* ll) {
|
||||
ll->head = NULL;
|
||||
ll->tail = NULL;
|
||||
}
|
||||
void ll_free(ll* ll) {
|
||||
ll_node* head = ll->head;
|
||||
while (head) {
|
||||
ll_node* to_free = head;
|
||||
head = head->next;
|
||||
free(to_free);
|
||||
}
|
||||
ll->tail = NULL;
|
||||
ll->head = NULL;
|
||||
}
|
||||
|
||||
libds_result ll_append(ll* ll, void* data) {
|
||||
libds_result result = LIBDS_SUCCESS;
|
||||
ll_node** to_set = ll->head ? &ll->tail->next : &ll->head;
|
||||
ll_node* new_node = malloc(sizeof(*new_node));
|
||||
if (new_node) {
|
||||
new_node->next = NULL;
|
||||
new_node->prev = ll->tail;
|
||||
new_node->data = data;
|
||||
|
||||
*to_set = new_node;
|
||||
ll->tail = new_node;
|
||||
} else {
|
||||
result = LIBDS_MALLOC;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
libds_result ll_prepend(ll* ll, void* data) {
|
||||
libds_result result = LIBDS_SUCCESS;
|
||||
ll_node** to_set = ll->tail ? &ll->head->prev : &ll->tail;
|
||||
ll_node* new_node = malloc(sizeof(*new_node));
|
||||
if (new_node) {
|
||||
new_node->next = ll->head;
|
||||
new_node->prev = NULL;
|
||||
new_node->data = data;
|
||||
|
||||
*to_set = new_node;
|
||||
ll->head = new_node;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
void ll_remove(ll* ll, void* data) {
|
||||
ll_node* head = ll->head;
|
||||
while (head) {
|
||||
if (head->data == data) {
|
||||
ll_node* to_delete = head;
|
||||
|
||||
head = head->next;
|
||||
*(to_delete->prev ? &to_delete->prev->next : &ll->head) = to_delete->next;
|
||||
*(to_delete->next ? &to_delete->next->prev : &ll->tail) = to_delete->prev;
|
||||
free(to_delete);
|
||||
} else {
|
||||
head = head->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* ll_find(ll* ll, void* data, compare_func compare) {
|
||||
void* to_return = NULL;
|
||||
ll_node* head = ll->head;
|
||||
while (head && to_return == NULL) {
|
||||
if (compare(data, head->data)) {
|
||||
to_return = head->data;
|
||||
}
|
||||
}
|
||||
return to_return;
|
||||
}
|
||||
int ll_foreach(ll* ll, void* data, compare_func compare, foreach_func foreach,
|
||||
...) {
|
||||
int return_code = 0;
|
||||
ll_node* head = ll->head;
|
||||
va_list args;
|
||||
while (head && return_code == 0) {
|
||||
if (compare(data, head->data)) {
|
||||
va_start(args, foreach);
|
||||
return_code = foreach (head->data, args);
|
||||
va_end(args);
|
||||
}
|
||||
head = head->next;
|
||||
}
|
||||
return return_code;
|
||||
}
|
||||
|
||||
void* ll_head(ll* ll) { return ll->head ? ll->head->data : NULL; }
|
||||
void* ll_tail(ll* ll) { return ll->tail ? ll->tail->data : NULL; }
|
||||
void* ll_pophead(ll* ll) {
|
||||
void* to_return = NULL;
|
||||
if (ll->head) {
|
||||
ll_node* to_delete = ll->head;
|
||||
to_return = to_delete->data;
|
||||
*(to_delete->next ? &to_delete->next->prev : &ll->tail) = NULL;
|
||||
ll->head = to_delete->next;
|
||||
free(to_delete);
|
||||
}
|
||||
return to_return;
|
||||
}
|
||||
void* ll_poptail(ll* ll) {
|
||||
void* to_return = NULL;
|
||||
if (ll->tail) {
|
||||
ll_node* to_delete = ll->tail;
|
||||
to_return = to_delete->data;
|
||||
*(to_delete->prev ? &to_delete->prev->next : &ll->head) = NULL;
|
||||
ll->tail = to_delete->prev;
|
||||
free(to_delete);
|
||||
}
|
||||
return to_return;
|
||||
}
|
108
src/main.c
108
src/main.c
|
@ -3,6 +3,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "ht.h"
|
||||
#include "ll.h"
|
||||
#include "vec.h"
|
||||
|
||||
int _test_vec_foreach_func(void* data, va_list args) {
|
||||
|
@ -145,9 +146,9 @@ int test_ht_foreach() {
|
|||
int return_code;
|
||||
int sum = 0;
|
||||
ht test_ht;
|
||||
ht_init(&test_ht);
|
||||
char* test_one = "one";
|
||||
char* test_two = "two";
|
||||
ht_init(&test_ht);
|
||||
ht_put(&test_ht, test_one, test_two);
|
||||
ht_put(&test_ht, test_two, test_one);
|
||||
ht_foreach(&test_ht, NULL, compare_always, _test_ht_foreach_func, &sum);
|
||||
|
@ -156,6 +157,101 @@ int test_ht_foreach() {
|
|||
return return_code;
|
||||
}
|
||||
|
||||
int _test_ll_foreach_count(void* data, va_list args){
|
||||
(*va_arg(args, int*))++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_ll_basic() {
|
||||
ll test_ll;
|
||||
ll_init(&test_ll);
|
||||
ll_free(&test_ll);
|
||||
return 1;
|
||||
}
|
||||
int test_ll_append() {
|
||||
int return_code;
|
||||
ll test_ll;
|
||||
char* test_string = "test";
|
||||
ll_init(&test_ll);
|
||||
ll_append(&test_ll, test_string);
|
||||
return_code = test_ll.head->data == test_string && test_ll.tail->data == test_string;
|
||||
ll_free(&test_ll);
|
||||
return return_code;
|
||||
}
|
||||
int test_ll_prepend() {
|
||||
int return_code;
|
||||
ll test_ll;
|
||||
char* test_string = "test";
|
||||
ll_init(&test_ll);
|
||||
ll_prepend(&test_ll, test_string);
|
||||
return_code = test_ll.head->data == test_string && test_ll.tail->data == test_string;
|
||||
ll_free(&test_ll);
|
||||
return return_code;
|
||||
}
|
||||
int test_ll_remove() {
|
||||
int return_code;
|
||||
ll test_ll;
|
||||
char* test_string = "test";
|
||||
ll_init(&test_ll);
|
||||
ll_prepend(&test_ll, test_string);
|
||||
ll_prepend(&test_ll, test_string);
|
||||
ll_remove(&test_ll, test_string);
|
||||
return_code = test_ll.tail == NULL && test_ll.head == NULL;
|
||||
ll_free(&test_ll);
|
||||
return return_code;
|
||||
}
|
||||
int test_ll_find() {
|
||||
int return_code;
|
||||
ll test_ll;
|
||||
char* test_string = "test";
|
||||
ll_init(&test_ll);
|
||||
ll_prepend(&test_ll, test_string);
|
||||
return_code = ll_find(&test_ll, "test", _test_vec_find_string) == test_string;
|
||||
ll_free(&test_ll);
|
||||
return return_code;
|
||||
}
|
||||
int test_ll_foreach(){
|
||||
int return_code;
|
||||
int count = 0;
|
||||
ll test_ll;
|
||||
char* test_string = "test";
|
||||
ll_init(&test_ll);
|
||||
ll_append(&test_ll, test_string);
|
||||
ll_append(&test_ll, test_string);
|
||||
ll_foreach(&test_ll, NULL, compare_always, _test_ll_foreach_count, &count);
|
||||
return_code = count == 2;
|
||||
ll_free(&test_ll);
|
||||
return return_code;
|
||||
}
|
||||
int test_ll_pophead(){
|
||||
int return_code;
|
||||
ll test_ll;
|
||||
char* test_string_one = "test 1";
|
||||
char* test_string_two = "test 2";
|
||||
ll_init(&test_ll);
|
||||
ll_append(&test_ll, test_string_one);
|
||||
ll_append(&test_ll, test_string_two);
|
||||
return_code = ll_pophead(&test_ll) == test_string_one
|
||||
&& ll_head(&test_ll) == ll_tail(&test_ll)
|
||||
&& ll_head(&test_ll) == test_string_two;
|
||||
ll_free(&test_ll);
|
||||
return return_code;
|
||||
}
|
||||
int test_ll_poptail(){
|
||||
int return_code;
|
||||
ll test_ll;
|
||||
char* test_string_one = "test 1";
|
||||
char* test_string_two = "test 2";
|
||||
ll_init(&test_ll);
|
||||
ll_append(&test_ll, test_string_one);
|
||||
ll_append(&test_ll, test_string_two);
|
||||
return_code = ll_poptail(&test_ll) == test_string_two
|
||||
&& ll_head(&test_ll) == ll_tail(&test_ll)
|
||||
&& ll_head(&test_ll) == test_string_one;
|
||||
ll_free(&test_ll);
|
||||
return return_code;
|
||||
}
|
||||
|
||||
int run_test(char* test_name, int (*test_func)()) {
|
||||
int success = test_func();
|
||||
printf("Running test %-15s . . . ", test_name);
|
||||
|
@ -164,18 +260,18 @@ int run_test(char* test_name, int (*test_func)()) {
|
|||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
char* test_names[11] = {"vec_basic", "vec_add", "vec_remove", "vec_find",
|
||||
char* test_names[19] = {"vec_basic", "vec_add", "vec_remove", "vec_find",
|
||||
"vec_foreach", "vec_index", "ht_basic", "ht_put",
|
||||
"ht_get", "ht_remove", "ht_foreach"};
|
||||
"ht_get", "ht_remove", "ht_foreach", "ll_basic", "ll_append", "ll_prepend", "ll_remove", "ll_find" , "ll_foreach", "ll_pophead", "ll_poptail"};
|
||||
|
||||
int (*test_functions[11])() = {
|
||||
int (*test_functions[19])() = {
|
||||
test_vec_basic, test_vec_add, test_vec_remove, test_vec_find,
|
||||
test_vec_foreach, test_vec_index, test_ht_basic, test_ht_put,
|
||||
test_ht_get, test_ht_remove, test_ht_foreach};
|
||||
test_ht_get, test_ht_remove, test_ht_foreach, test_ll_basic, test_ll_append, test_ll_prepend, test_ll_remove, test_ll_find, test_ll_foreach, test_ll_pophead, test_ll_poptail};
|
||||
|
||||
int test_index = 0;
|
||||
int result = 1;
|
||||
for (; test_index < 11 && result; test_index++) {
|
||||
for (; test_index < 19 && result; test_index++) {
|
||||
result = run_test(test_names[test_index], test_functions[test_index]);
|
||||
}
|
||||
return result ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
|
|
Loading…
Reference in New Issue
Block a user