Implement linked list data structure.

This commit is contained in:
Danila Fedorin 2016-12-23 22:39:10 -08:00
parent 27ea69e125
commit 4fa9ab13ee
4 changed files with 330 additions and 7 deletions

View File

@ -4,7 +4,7 @@ project(libds)
set_property(GLOBAL PROPERTY C_STANDARD 90) set_property(GLOBAL PROPERTY C_STANDARD 90)
ADD_COMPILE_OPTIONS(-pedantic -Wall) 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) add_executable(libds src/main.c)
target_include_directories(ds PUBLIC include) target_include_directories(ds PUBLIC include)

113
include/ll.h Normal file
View 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
View 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;
}

View File

@ -3,6 +3,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "ht.h" #include "ht.h"
#include "ll.h"
#include "vec.h" #include "vec.h"
int _test_vec_foreach_func(void* data, va_list args) { int _test_vec_foreach_func(void* data, va_list args) {
@ -145,9 +146,9 @@ int test_ht_foreach() {
int return_code; int return_code;
int sum = 0; int sum = 0;
ht test_ht; ht test_ht;
ht_init(&test_ht);
char* test_one = "one"; char* test_one = "one";
char* test_two = "two"; char* test_two = "two";
ht_init(&test_ht);
ht_put(&test_ht, test_one, test_two); ht_put(&test_ht, test_one, test_two);
ht_put(&test_ht, test_two, test_one); ht_put(&test_ht, test_two, test_one);
ht_foreach(&test_ht, NULL, compare_always, _test_ht_foreach_func, &sum); ht_foreach(&test_ht, NULL, compare_always, _test_ht_foreach_func, &sum);
@ -156,6 +157,101 @@ int test_ht_foreach() {
return return_code; 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 run_test(char* test_name, int (*test_func)()) {
int success = test_func(); int success = test_func();
printf("Running test %-15s . . . ", test_name); 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) { 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", "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_basic, test_vec_add, test_vec_remove, test_vec_find,
test_vec_foreach, test_vec_index, test_ht_basic, test_ht_put, 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 test_index = 0;
int result = 1; 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]); result = run_test(test_names[test_index], test_functions[test_index]);
} }
return result ? EXIT_SUCCESS : EXIT_FAILURE; return result ? EXIT_SUCCESS : EXIT_FAILURE;