From 6fa8c4a110fab5e34145b8710eb02b45c7c0b9b8 Mon Sep 17 00:00:00 2001 From: William Brawner Date: Sun, 17 Nov 2024 22:16:17 -0700 Subject: [PATCH] Initial commit --- .gitignore | 2 + Makefile | 10 ++++ include/doubly-linked-list.h | 43 +++++++++++++++++ meson.build | 5 ++ src/doubly-linked-list.c | 72 ++++++++++++++++++++++++++++ test/dll.c | 93 ++++++++++++++++++++++++++++++++++++ 6 files changed, 225 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 include/doubly-linked-list.h create mode 100644 meson.build create mode 100644 src/doubly-linked-list.c create mode 100644 test/dll.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9785597 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +.cache diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9c85cd3 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +BUILDDIR="build" + +all: configure + meson compile -C $(BUILDDIR) + +configure: + meson setup $(BUILDDIR) + +test: configure + meson test -vC $(BUILDDIR) diff --git a/include/doubly-linked-list.h b/include/doubly-linked-list.h new file mode 100644 index 0000000..c26204c --- /dev/null +++ b/include/doubly-linked-list.h @@ -0,0 +1,43 @@ +struct dll_node { + struct dll_node *previous; + struct dll_node *next; + char *value; +}; + +typedef struct dll_node dll_node; + +typedef struct { + struct dll_node *head; +} dll_list; + +/** + * Allocates a new (empty) doubly linked list + */ +dll_list *dll_list_new(); + +/** + * Frees the memory used by a list, including all of its child nodes + */ +void dll_list_free(dll_list *list); + +/** + * Inserts a given node at the given index of the given list. If index is -1 +* or is greater than the size of the list, then the node will be inserted at the end of the list + */ +void dll_list_insert(dll_list *list, dll_node *node, int index); + +/** + * Appends a node to the end of a given list + */ +void dll_list_append(dll_list *list, dll_node *node) { + dll_list_insert(list, node, -1); +} + +// TODO: find and delete operations on list + +dll_node *dll_node_new(char *value); + +/** + * Frees the memory for a given node, returning a pointer to the next node, if any + */ +dll_node *dll_node_free(dll_node *node); diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..4697c43 --- /dev/null +++ b/meson.build @@ -0,0 +1,5 @@ +project('clox', 'c') +incdir = include_directories('include') +dll_lib = library('dll', 'src/doubly-linked-list.c', include_directories: incdir) +dll_test = executable('dll_test', 'test/dll.c', include_directories: incdir, link_with: dll_lib) +test('doubly-linked list tests', dll_test) diff --git a/src/doubly-linked-list.c b/src/doubly-linked-list.c new file mode 100644 index 0000000..691e25f --- /dev/null +++ b/src/doubly-linked-list.c @@ -0,0 +1,72 @@ +#include "doubly-linked-list.h" +#include +#include +#include +#include + +dll_list *dll_list_new() { + dll_list *list = malloc(sizeof(dll_list)); + list->head = NULL; + return list; +} + +void dll_list_free(dll_list *list) { + dll_node *current_node = list->head; + while (current_node != NULL) { + current_node = dll_node_free(current_node); + } + + free(list); +} + +void dll_list_insert(dll_list *list, dll_node *node, int index) { + if (list->head == NULL) { + list->head = node; + return; + } + + if (index == 0) { + dll_node *old_head = list->head; + list->head = node; + node->next = old_head; + return; + } + + int current_index = 0; + dll_node *current_node = list->head; + while (current_node->next != NULL) { + current_index++; + if (index > -1 && index == current_index) { + break; + } + current_node = current_node->next; + } + dll_node *next_node = current_node->next; + if (next_node != NULL) { + next_node->previous = node; + node->next = next_node; + } + node->previous = current_node; + current_node->next = node; +} + +dll_node *dll_node_new(char *value) { + dll_node *node = malloc(sizeof(dll_node)); + node->previous = NULL; + node->next = NULL; + int value_length = strlen(value) * sizeof(char); + value_length++; // pad for null byte + node->value = malloc(value_length); + strcpy(node->value, value); + node->value[value_length - 1] = '\0'; + return node; +} + +dll_node *dll_node_free(dll_node *node) { + dll_node *next = node->next; + free(node->value); + node->previous = NULL; + node->next = NULL; + free(node); + return next; +} diff --git a/test/dll.c b/test/dll.c new file mode 100644 index 0000000..64f4aa7 --- /dev/null +++ b/test/dll.c @@ -0,0 +1,93 @@ +#include "doubly-linked-list.h" +#include +#include +#include +#include + +dll_list *list = NULL; +dll_node *head = NULL; +char *list_string = NULL; + +#define ASSERT_STR_EQ(expected, actual) \ + { \ + if (strcmp(expected, actual) != 0) { \ + fprintf(stderr, \ + "strings don't match. expected: \"%s\", actual: \"%s\"\n", \ + expected, actual); \ + cleanup(); \ + return 1; \ + } \ + } + +char *list_to_string(dll_list *list) { + char *list_string = malloc(sizeof(char)); + list_string[0] = '\0'; + dll_node *current_node = list->head; + while (current_node != NULL) { + // TODO: There's probably a better way to do this. Find it + int list_string_length = strlen(list_string); + int new_length = list_string_length + strlen(current_node->value) + 1; + if (current_node->previous != NULL) { + new_length += 2; // Add space for another ", " + } + char *new_list_string = realloc(list_string, new_length); + if (new_list_string == NULL) { + fprintf(stderr, "error: failed to reallocate memory for list string\n"); + return NULL; + } else { + list_string = new_list_string; + } + if (list_string_length > 0 && current_node->previous != NULL) { + strcpy(&list_string[list_string_length], ", "); + list_string_length += 2; + } + strcpy(&list_string[list_string_length], current_node->value); + list_string[new_length - 1] = '\0'; + current_node = current_node->next; + } + return list_string; +} + +void cleanup() { + if (list != NULL) { + dll_list_free(list); + list = NULL; + } + + if (head != NULL) { + dll_node_free(head); + head = NULL; + } + + if (list_string != NULL) { + free(list_string); + list_string = NULL; + } +} + +int main(int argc, char **argv) { + list = dll_list_new(); + dll_node *head = dll_node_new("0"); + dll_list_append(list, head); + head = NULL; + // TODO: Extract test utilities to shared file + list_string = list_to_string(list); + ASSERT_STR_EQ("0", list_string); + + dll_node *third = dll_node_new("2"); + dll_list_insert(list, third, 2); + + free(list_string); + list_string = list_to_string(list); + ASSERT_STR_EQ("0, 2", list_string); + + dll_node *second = dll_node_new("1"); + dll_list_insert(list, second, 1); + + free(list_string); + list_string = list_to_string(list); + ASSERT_STR_EQ("0, 1, 2", list_string); + + cleanup(); + return 0; +}